Summary of Spring IOC knowledge points

Posted by lyealain on Sun, 14 Jun 2020 07:17:49 +0200

@Configuration configure spring and start the spring container

@Configuration is used to define the configuration class, which is labeled on the class, which is equivalent to < beans > in the xml configuration file of spring. Its function is to configure the spring container (application context)

Example description:

Configuration class

@Configuration
public class MyConfig {
    public MyConfig() {
        System.out.println("TestConfig Container initialization!!!!");
    }
}

Equivalent to xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

Test class

public class TestConfig {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfig.class);
    }
}

Operation results

TestConfig container initialization!!!!

@ Configuration&@Bean Register components in container

@Bean is marked in method, which is equivalent to < / bean > in xml configuration configuration

Code example description:

Registration class

public class ConfigurationBean {
    public void start() {
        System.out.println("Start container initialization!!!!!");
    }
}

Configuration class

@Configuration
public class MyConfiguration {
    //You can specify the name of the bean or the method loaded before the container is injected
    @Bean()
     public ConfigurationBean configurationBean(){
         return new ConfigurationBean();
     }

}

Equivalent to xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="configurationBean" class="zfcoding.bean.ConfigurationBean" ></bean> 
</beans>

test method

@Test
    public void ConfigurationTest(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
        ConfigurationBean configurationBean=(ConfigurationBean) applicationContext.getBean("configurationBean");
        System.out.println(configurationBean);
    }

Operation result:

Start container initialization!!!!!
zfcoding.bean.ConfigurationBean@77be656f

Explain:

(1) The @ bean annotation is marked on the method. If the bean name is not specified through @ bean, the method name is the same by default;

(2) The @ bean annotation is marked on the method. If you want to specify the bean name through @ bean, for example, @ bean ("configurationbean 1"), the name of the instantiated bean in the container is configurationbean 1;

@Bean specifies the method of initialization and destruction and @ Scope, @ Lazy

Life cycle of container bean Management:

We can customize the initialization and destruction methods. The container calls our customized initialization and destruction methods when the bean is in the current life cycle.

@The initialization and destruction methods are specified in the Bean.

@Bean(initMethod = "init",destroyMethod = "destory")

xml

<bean id="student" class="zfcoding.bean.Student"  init-method="init" destroy-method="destory"></bean>

@Scope

By default: singleton single instance (the default), ioc container startup will call methods to create objects and put them into ioc container. Get the object directly every time later.

prototype: multi instance, ioc container startup does not call methods to create objects in the container. Instead, the method is called to create the object every time it is acquired, and the object is created once.

@Lazy loading,

For single instance bean s, objects are created by default when the container is started

Lazy loading: the container starts the non creation key object, creates the object with Bean for the first time, and initializes it.

Example description:

public class Student {
    
    public Student() {
        System.out.println("Student Container loading starts!!!!");
    }
    //The object is created and assigned. Call the initialization method
    public void init() {
        System.out.println("Student.init()Initialization start.....");
    }
    //Destroy is called when the container is closed for (single instance).
    //Multi instance (@ Scope("prototype"): the container will not manage this bean, and the container will not call back the destroyed method.
    public void destory() {
        System.out.println("Student.destory().........");
    }
}
@Configuration
public class MyConfigStudent {
    //@Lazy
    //@Scope("prototype")
    @Bean(value = "student",initMethod = "init",destroyMethod = "destory")
    public Student student(){
        return new Student();
    }
}

Test class I

public class MyConfigStudentTest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigStudent.class);
    }
}

Operation results

Student container loading started!!!!
Student.init() initialization start

Then, the ioc container startup will call methods to create objects and put them into the ioc container. Get the object directly every time later.

If we use @ Lazy annotation (single instance), the container starts the non creation key object, and the test result has no output information.

Test class 2. For the first time, we use Bean to create objects and initialize them

public class MyConfigStudentTest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigStudent.class);
        Student student = (Student)applicationContext.getBean("student");
        System.out.println(student);
        applicationContext.close();
    }
}

Operation results

Student Container loading starts!!!!
Student.init()Initialization start.....
zfcoding.bean.Student@7364985f
Student.destory().........

Lazy load (@ lazy): when the container starts, it does not create key objects, but creates objects with Bean for the first time and initializes them.

InitializingBean and DisposableBean

The InitializingBean interface provides a way for beans to initialize methods. It only includes the afterpropertieset method. Any class that inherits this interface will execute this method when initializing beans.

Test class

public class TestInitializingBean implements InitializingBean {
    public void afterPropertiesSet() throws Exception {
        System.out.println("TestInitializingBean.afterPropertiesSet()......");
    }

    public void init() {
        System.out.println("TestInitializingBean.init().......");
    }
}

Configuration class

@Configuration
public class MyInitializingBeanConfig {
    @Bean(value = "testInitializingBean",initMethod = "init")
    public TestInitializingBean testInitializingBean(){
        return new TestInitializingBean();
    }
}

Test class

public class TestnitializingBeanConfig {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyInitializingBeanConfig.class);
    }
}

Operation result:

TestInitializingBean.afterPropertiesSet()......
TestInitializingBean.init().......

It can be seen from the results that when Spring initializes a bean, if the bean implements the InitializingBean interface and specifies init method in the configuration file, the system calls the afterPropertieSet() method first, and then the method specified in init method.

How is this method implemented in spring? Check the invokeInitMethods() in the spring load bean's source class AbstractAutowiredCapableBeanFactory class, as follows:

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
            throws Throwable {
       //Determine whether the bean implements the InitializingBean interface. If the InitializingBean interface is implemented, only the afterPropertiesSet method of the bean will be called
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

        if (mbd != null) {
            String initMethodName = mbd.getInitMethodName();
            if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

Summary:

Spring provides two ways to initialize beans. One is to implement the InitializingBean interface and override the afterpropertieset method. The other is to specify it in the configuration file through init method, but if it is specified at the same time, the method implemented by InitializingBean is called first.

@PostConstruct and @ PreDestroy

have access to JS250,@PostConstruct Mark on the method, the bean is created and the property assignment is completed to execute the initialization method

@PreDestroy, inform us to clean up before the container destroys bean s

@ ComponentScan&@Configuration Register components with container

@ Configuration+@ComponentScan Inject Component, just annotate @ Controller, @ Service, @ Repository, @ Component into Spring container

xml configuration

<context:component-scan base-package="zfcoding"></context:component-scan>

Code example description:

Classes to inject

@Component
public class PersonComponent {
}
@Repository
public class PersonDao {
}
@Service
public class PersonService {
}
@Controller
public class PersonController {
}

Configuration class


@Configuration
//@ComponentScan(basePackages = "zfcoding",
////        includeFilters =@ComponentScan.Filter(type=FilterType.ANNOTATION,classes = Controller.class),useDefaultFilters=false )
@ComponentScan(basePackages = "zfcoding",
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}))
public class MyCompoentScan {

}

Test method

 @Test
    public void ComponentSanTest(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyCompoentScan.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String definitionName:definitionNames){
            System.out.println(definitionName);
        }
    }

Operation results

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myCompoentScan
personComponent
personDao

explain:

excludeFilters exclude components that do not need to be loaded

includeFilters only contain components that need to be loaded. When using them, useDefaultFilters=false to take effect

@ ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class }), excluding the annotation annotation annotation of @ controller and @ service classes.

@Conditional - set component scope

Judge according to certain conditions, and register bean s in the container according to the conditions

To customize Conditional conditions, you need to write a class that implements the Condition interface. We can directly use @ Conditional({WindowsEnvironment.class }

@Conditional can be defined on both methods and classes. Code implementation of custom conditions requires the implementation of Condition interface

Conditional class

public class WindowsEnvironment implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        BeanDefinitionRegistry registry = context.getRegistry();
        if(property.contains("Windows 10")){
           return true;
        }
        return false;
    }
}
public class LiunxEnvironment implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        if (environment.containsProperty("Liunx")){
            return true;
        }
        return false;
    }
}

Configuration class

@Configuration
public class MyCondition {

    @Conditional({WindowsEnvironment.class})
    @Bean
    public ConfigurationBean configurationBean(){
        return new ConfigurationBean("Zhang San");
    }
    @@Conditional({LiunxEnvironment.class})
    @Bean
    public ConfigurationBean configurationBean1(){
        return new ConfigurationBean("Li Si");
    }
}

test method

public class CoditionTest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyCondition.class);
        String[] beanNamesForType = applicationContext.getBeanNamesForType(ConfigurationBean.class);
        for (String s:beanNamesForType){
            System.out.println(s);
        }
    }
}

Operation results

configurationBean

@Import quickly import a component to a container

1. @ import (the component to be imported), the container will automatically register this component, and the default id is the full class name.

2. ImportSelector, the custom logic returns the classology written by the component to be imported to implement the ImportSelector interface, and returns the full class name array of the primary key to be imported.

3. Importbeandefinitionregister. For custom logic, you need to write a class to implement the importbeandefinitionregister interface, and register the beans into the container manually.

Code instance

public class Red {
}
public class Blue {
}
//
@Import({Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyImport {
   
}
public class MyImportSelector implements ImportSelector {

    //AnnotationMetadata: all annotation information of the current annotation @ Import annotation class
    //Returns the full class name of the class
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return new String[]{"zfcoding.bean.Blue"};
    }
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
     //importingClassMetadata: annotation information of the current class
    //Beandefinitionregistry beandefinition register class to add all beans to the container, be anDefinitionRegistry.registerBeanDefinition Manual registration

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean red = registry.containsBeanDefinition("zfcoding.bean.Red");
        if (red){
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Blue.class);
            registry.registerBeanDefinition("blue",rootBeanDefinition);
        }

    }
}

Test class

public class ImportTest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyImport.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String s:beanDefinitionNames){
            System.out.println(s);
        }
    }
}

Operation results

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactorymyImport
zfcoding.bean.Red
zfcoding.bean.Blue
blue

Summary:

To import components into a container:

​ 1,@ Configuration&@Bean Register components in container

​ 2,@ComponentScan&@Configuration(@Controller ,@Service, @Repository, @Component)

3, @ Import quickly imports a component to the container

@Value assignment and @ PropertySource load external configuration file

@Value

1. Basic value

​ 2,SPEL #{}

3. You can write ${} to the value in the configuration file (the value in the running environment)

Use @ PropertySource to get the value in the external configuration file and save it to the running environment. After loading the final configuration file, use ${} to extract the values in the configuration file

Code instance:

The code omits the setter(),getter(),toString() method.

public class Person {
    @Value("Zhang San")
    private String name;
    @Value("#{20-1}")
    private Integer age;
    @Value("${person.sex}")
    private String sex;
}
//<context:property-placeholder location="person.properties"></context:property-placeholder>
@PropertySource("classpath:person.properties")
@Configuration
public class MyConfigValue {
      @Bean
      public Person person(){
          return new Person();
      }
}

person.properties

person.sex=1

Test class

@Test
    public void test1(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigValue.class);
        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);
    }

Operation results

Person{name='Zhang San', age=19, sex='1'}

@Autowired &@Qualifier&@Primary

Automatic assembly:

@Autowired auto injection, a single default priority is to find the corresponding component in the container according to the type. By default, the property must be assigned well. If not found, an error will be reported. You can use @ Autowired (required=false), which is not necessary;

@Qualifier, which specifies the component id to be assembled;

@When Primary allows Spring to automatically assemble, you can use @ Qualifier to specify the name of the bean to be assembled

@Resource &@Inject

Spring also supports @ Resource (JSR250) and @ Inject (JSR330) {java specification annotations}

@Resource and @ Autowired implement the same function of automatic assembly. By default, they are assembled according to the name of the component.

@Inject needs to be imported javax.inject The package of is the same as the @ Autowired function, without the function of required=false.

@Profile environment construction

Spring provides us with the ability to dynamically activate and switch a series of components according to the current environment

Let's use the data source to illustrate this problem:

@Profile specifies which environment can be registered in the container. With @ profile added, only when this environment is activated can it be registered in the spring container. The default is defalut

Code instance:

@PropertySource("classpath:db.properties")
@Configuration
public class MyConfigProfile  implements EmbeddedValueResolverAware {
    @Value("${db.username}")
    private String username;
    @Value("${db.password=}")
    private String password;

    private StringValueResolver stringValueResolver;

    private String driveClass;

    @Profile("default")
    @Bean("testDataSource")
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setUser(username);
        dataSource.setPassword(password);
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8");
        dataSource.setDriverClass(driveClass);
        return dataSource;
    }
    @Profile("dev")
    @Bean("devDataSource")
    public DataSource devDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setUser(username);
        dataSource.setPassword(password);
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8");
        dataSource.setDriverClass(driveClass);
        return dataSource;
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.stringValueResolver=resolver;
        driveClass = stringValueResolver.resolveStringValue("db.driverClass");
    }
}

db.properties

db.username=root
db.password=root
db.driverClass=com.mysql.jdbc.Driver

Test code:

public class DataSourceProfileTest {
    @Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigProfile.class);
        String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String s:beanNamesForType){
            System.out.println(s);
        }
    }
    
    /* Using a parameterless constructor
     * Set up the active environment
     * Register main configuration class
     * Start refresh container
     */
    @Test
    public void test1(){
        //Using a parameterless constructor 
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext();
        //Set up the active environment
        applicationContext.getEnvironment().setActiveProfiles("dev");
        //Register main configuration class
        applicationContext.register(MyConfigProfile.class);
        //Start refresh container
        applicationContext.refresh();
        String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String s:beanNamesForType){
            System.out.println(s);
        }
    }
}

Test case run results

testDataSource

devDataSource

Summary:

@The Profile can also be marked on the class to indicate that the configuration in the entire configuration file can take effect only when the environment is specified. Beans without environment representation are loaded in any environment (provided that the configuration class is valid). You can also use command line parameters:- Dspring.profiles.active=test Activate the corresponding environment.

I am AFP, the author of the official account of "AFP chat programming". I am a learning enthusiast for back-end technology. I will update JAVA technical articles regularly, and encourage me on the way forward.

Welcome to my official account, back 666, receive benefits, you know.

Topics: Java Spring xml JDBC MySQL