SpringBoot automatic assembly principle

summary

detail

Traditional Spring projects have many configuration files. For example, if we want to use Redis, we generally need to configure JedisConnectionFactory, JedisPoolConfig, and RedisTemplate in application.xml in addition to the corresponding dependent jar packages. But if you use SpringBoot, the system will automatically generate these classes based on the jar package in pom.xml and inject them into the IOC container.

  1. Configuration is required in traditional Spring projects
<bean id="jedisConnectionFactory" class="...JedisConnectionFactory"></bean>
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"></bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"></bean>
  1. When using SpringBoot, in addition to introducing the corresponding jar package in pom.xml, you only need to configure the corresponding property values ​​in application.properties.

Take Redis as an example

  1. Obtain more than 120 default function configuration classes from spring-boot-autoconfigure.jar/META-INF/spring.factories , including the fully qualified name of the redis function configuration class RedisAutoConfiguration. Generally, a function configuration class surrounds this function and is responsible for management Create multiple related functional classes, for example, RedisAutoConfiguration is responsible for the creation of three functional classes: JedisConnectionFactory, RedisTemplate, StringRedisTemplate
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
  1. One condition for the RedisAutoConfiguration configuration class to take effect is @ConditionalOnClass: JedisConnection.class, RedisOperations.class, Jedis.class, so it will go to the classpath to find the corresponding class file
@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {}
  1. If pom.xml has a corresponding jar package, it can match the corresponding dependent classes: JedisConnection.class, RedisOperations.class, Jedis.class
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  1. If the match is successful, this function configuration class will take effect, and the default property configuration class @EnableConfigurationProperties(RedisProperties.class) will be injected at the same time
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    private int database = 0;
    private String url;
    private String host = "localhost";
    private String password;
    private int port = 6379;
  1. The Redis function configuration will generate the final JedisConnectionFactory and RedisTemplate according to the conditions. The condition is that there is no user-defined @ConditionalOnMissingBean(RedisConnectionFactory.class), RedisTemplate in the IOC environment.
@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
        @Bean
        @ConditionalOnMissingBean(RedisConnectionFactory.class)
        public JedisConnectionFactory redisConnectionFactory()
                throws UnknownHostException {
            return applyProperties(createJedisConnectionFactory());
        }

        @Bean
        @ConditionalOnMissingBean(name = "redisTemplate")
        public RedisTemplate<Object, Object> redisTemplate(
                RedisConnectionFactory redisConnectionFactory)
                        throws UnknownHostException {
            RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }
}
  1. The final default assembly class created will be injected into the IOC through the @Bean annotation in the functional configuration class

Principle and result analysis

Through various annotations, SpringApplication.run(Application.class, args) will read the spring.factories configuration file in spring-boot-autoconfigure.jar when it is running. The configuration file contains the className of the configuration class of all auto-assembly classes. , And then generate the Configuration class of the corresponding function. If these function configuration classes are to take effect, they will go to the classpath to find whether there is a dependency class of this class (that is, the pom.xml must have the jar package of the corresponding function), and then the configuration class Then the final function class is generated through judgment, and the default attribute value class is injected into the configuration class, and the function class can be referenced and assigned default values. The principle of generating functional classes is customization first, and auto-assembly classes will only be used when there is no customization.

In summary, if you want to automatically assemble a class, you need to meet two conditions:

There is a configuration class of this class in spring.factories (a configuration class can create multiple dependent classes around this function)
There needs to be a corresponding jar package in pom.xml

The result of the whole process is two things:

  1. According to various judgments and dependencies, the classes required by the business are finally generated and injected into the IOC container
  2. The classes generated by auto-wiring are given some default attribute values

Dependent annotations

  • @SpringBootApplication: The annotations of the sb project application startup class are actually a combination of three annotations: @SpringBootConfiguration, @EnableAutoConfiguration , @ComponentScan , of which the second one plays a role in automatic assembly
  • @EnableAutoConfiguration: Indicates the function of SB application to start automatic assembly (including loading the corresponding Bean into the IOC container, and assigning values ​​to attributes according to the default configuration)
  • @Import(EnableAutoConfigurationImportSelector.class): This annotation is more powerful. Beans that are not registered in the IOC can be forcibly registered in the IOC, which means that the automatic configuration function needs to be imported into EnableAutoConfigurationImportSelector.class.
  • @Configuration
  • @ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class }): indicates that if the RedisAutoConfiguration configuration class works, there must be a jar package containing these classes.
  • @EnableConfigurationProperties(RedisProperties.class): Indicates that the configuration in RedisProperties.class is referenced by default
  • @ConditionalOnMissingBean(RedisConnectionFactory.class): This is a very powerful annotation. Customization takes precedence when implementing automatic assembly. It means that if the user does not customize the RedisConnectionFactory.class class, the default JedisConnectionFactory will be used.

Automated assembly process

  1. The dependencies between classes are realized through various annotations. When the container starts, Application.run will call the selectImports method of EnableAutoConfigurationImportSelector.class (actually the method of its parent class)
  2. The selectImports method will eventually call the SpringFactoriesLoader.loadFactoryNames method to get a comprehensive list of commonly used BeanConfiguration
  3. The loadFactoryNames method will read FACTORIES_RESOURCE_LOCATION (that is, spring.factories under spring-boot-autoconfigure.jar), and get the fully qualified name ClassName of all Spring-related Beans, about 120
  4. The selectImports method continues to call filter(configurations, autoConfigurationMetadata); at this time, it will filter one by one according to the conditions in these BeanConfigurations. The most important thing is
    @ConditionalOnClass. This condition annotation will be searched under the classpath. Whether there is this condition dependency in the jar package Class, so you must have the corresponding jar package to have these dependent classes, and then generate some default configuration beans required by the IOC environment
  5. Finally, inject the qualified BeanConfiguration into the attribute value of the default EnableConfigurationPropertie class and inject it into the IOC environment

Reference

  1. Brief book-springBoot automatic assembly principle