Spring Boot 2.x source code series 2-spring boot automatic configuration

1. Frontier

In the Spring Boot startup process article, we learned about the startup process of Spring Boot, in which all automated configuration classes are loaded during the refresh context

The automatic configuration loading of Spring Boot is divided into two steps:

1) First, in the initialization phase of Spring Boot, all configuration contents in all MATA-INF/spring.factories files are stored in the cache as a one-to-many collection of class names through SpringFactoriesLoader

The class set corresponding to EnableAutoConfiguration in the MATA-INF/spring.factories file in the spring-boot-autoconfigure module is as follows:

The collection of classes loaded into the cache is as follows:

2) Then, in the process of refreshing the context, obtain a collection of auto-configuration classes through the loadFactoryNames method of SpringFactoriesLoader, and finally obtain the class objects and construction methods of these classes through reflection to generate class instances

The process of creating an automated configuration class is as follows:

Two, automatic configuration flow chart

In order to better understand the automation configuration, a picture is used to illustrate, as shown in the following figure:

The META-INF directory of components such as mybatis-spring-boot-starter contains the spring.factories file, and SpringFactoriesLoader scans all the full names of the classes in the spring.factories files and returns an array of the full names of the classes, the full names of the returned classes By being instantiated by reflection, a concrete factory instance is formed, and the factory instance is used to generate the beans that the component specifically needs

Three, automatic configuration source code

It is the @EnableAutoConfiguration annotation that turns on the Spring Boot automatic configuration function . Let’s start with the @EnableAutoConfiguration annotation.

3.1 EnableAutoConfiguration

@EnableAutoConfiguration function is to enable SpringBoot automatic configuration , the source code is as follows:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自动配置包下的bean注册
@AutoConfigurationPackage
// AutoConfigurationImportSelector 自动配置导入组件,这个类是 Spring Boot 实现自动配置的核心
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	// 自动配置排除特定的类
	Class<?>[] exclude() default {};

	// 自动配置排除特定的类名
	String[] excludeName() default {};

}

From the @EnableAutoConfiguration annotation source code, we can see that the AutoConfigurationImportSelector class is the core of automatic configuration.

3.2  AutoConfigurationImportSelector

The AutoConfigurationImportSelector class structure is as follows:

The source code of the core method getAutoConfigurationEntry in AutoConfigurationImportSelector is as follows:

	// 1、AutoConfigurationImportSelector 的 getAutoConfigurationEntry 方法
	protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			// 没有开始自动配置,则返回空
			return EMPTY_ENTRY;
		}
		// 获取元数据属性,只有 exclude 和 excludeName
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 根据元数据获取所有配置类集合
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
		configurations = removeDuplicates(configurations);
		// 获取需要排除的配置类集合
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		// 移除需要排除的配置类集合
		configurations.removeAll(exclusions);
		// 根据配置的 AutoConfigurationImportFilter 过滤器进一步过滤掉不需要的配置类集合
		configurations = filter(configurations, autoConfigurationMetadata);
		// 发布自动配置导入事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		// 构建 AutoConfigurationEntry 对象返回
		return new AutoConfigurationEntry(configurations, exclusions);
	}


	// 2、AutoConfigurationImportSelector 的 getCandidateConfigurations 方法
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		// 获取 spring.factories 文件中 EnableAutoConfiguration 对应的配置类集合
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

After reading the core method implemented by AutoConfigurationImportSelector, is it to figure out where to call the method of AutoConfigurationImportSelector?

The answer is definitely called when the context is refreshed. The specific process depends on the automatic configuration class creation process.

3.3 Automatic configuration class creation process

The automatic configuration class creation goes through the following two processes:

1) First, use the invokeBeanFactoryPostProcessors method of AbstractApplicationContext to convert the configured auto-configuration class to BeanDefinition and register it in the BeanFactory. If the auto-configuration class needs to generate a proxy class, the CGLIB dynamic proxy will be used to directly generate the corresponding proxy class

2), then instantiate the BeanDefinition into the corresponding Bean through the finishBeanFactoryInitialization method of AbstractApplicationContext

3.3.1 Convert to BeanDefinition

The specific entry is in the invokeBeanFactoryPostProcessors method of AbstractApplicationContext. The debug process is shown in the following figure:

Debug specific stack information:

getAutoConfigurationEntry:127, AutoConfigurationImportSelector (org.springframework.boot.autoconfigure)
process:420, AutoConfigurationImportSelector$AutoConfigurationGroup (org.springframework.boot.autoconfigure)
getImports:878, ConfigurationClassParser$DeferredImportSelectorGrouping (org.springframework.context.annotation)
processGroupImports:804, ConfigurationClassParser$DeferredImportSelectorGroupingHandler (org.springframework.context.annotation)
process:774, ConfigurationClassParser$DeferredImportSelectorHandler (org.springframework.context.annotation)
parse:185, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:315, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:232, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:275, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:95, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:691, AbstractApplicationContext (org.springframework.context.support)
refresh:528, AbstractApplicationContext (org.springframework.context.support)
refresh:142, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:775, SpringApplication (org.springframework.boot)
refreshContext:397, SpringApplication (org.springframework.boot)
run:316, SpringApplication (org.springframework.boot)
main:29, MainApplication (com.springboot.demo)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
run:49, RestartLauncher (org.springframework.boot.devtools.restart)

After the invokeBeanFactoryPostProcessors method of AbstractApplicationContext, the automatic configuration classes in all spring.factories files are parsed into BeanDefinition, as shown in the following figure:

3.3.2 BeanDefinition generates Bean instance

After the BeanDefinition is generated, the BeanDefinition generates the corresponding Bean instance. For the creation of the Bean, we have already  analyzed the Bean loading of the Spring source code , so I won’t repeat it here.

Four, automatic configuration example analysis

Let's take RedisAutoConfiguration as an example to understand its principle through the source code

The source code of RedisAutoConfiguration is as follows:

// 表明是一个配置类,其所有被 @bean 注解的方法都会被 Spring 容器创建成 Bean 实例,
// proxyBeanMethods 属性表示是否是代理,默认为 true,如果需要生成代理类,则会调用 CGLIB 生成代理类实例
@Configuration(proxyBeanMethods = false)
// 依赖于 RedisOperations 类,必须先实例该类
@ConditionalOnClass(RedisOperations.class)
// 启动该类的 ConfigurationProperties 功能;将配置文件中对应的值和 RedisProperties 绑定起来;并把 RedisProperties 加入到 Spring 的 Environment 中去
// 例如配置文件中的 spring.redis.url、spring.redis.host 等属性值会绑定到 RedisProperties 对应的属性上去
@EnableConfigurationProperties(RedisProperties.class)
// 引入需要的配置类
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	// 缺少 RedisTemplate Bean 时会创建 RedisTemplate 实例,否则什么也不做
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	// 会生成 StringRedisTemplate 实例
	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

All the properties that can be configured in the configuration file are defined in the xxxProperties class. Which properties can be configured in the configuration file can refer to the property class corresponding to a certain function, such as @EnableConfigurationProperties(RedisProperties.class) in the RedisAutoConfiguration source code. The properties defined in the RedisProperties file are the properties we can configure in the configuration file

The source code of RedisProperties is as follows:

// 所有属性的前缀
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {

	/**
	 * Database index used by the connection factory.
	 */
	// 数据库,默认是 0
	private int database = 0;

	/**
	 * Connection URL. Overrides host, port, and password. User is ignored. Example:
	 * redis://user:[email protected]:6379
	 */
	// url
	private String url;

	/**
	 * Redis server host.
	 */
	// host 地址,默认是 localhost
	private String host = "localhost";

	/**
	 * Login password of the redis server.
	 */
	// 密码
	private String password;

	/**
	 * Redis server port.
	 */
	// 端口,默认是 6379
	private int port = 6379;

	/**
	 * Whether to enable SSL support.
	 */
	// 是否支持 ssl
	private boolean ssl;

	/**
	 * Connection timeout.
	 */
	// 超时时间
	private Duration timeout;
}

From the RedisProperties source code, you can configure spring.redis.url, spring.redis.host, spring.redis.password and other properties in the configuration file

Configure redis properties in the configuration file, as shown in the following figure:

All the properties in the red box on the right come from the RedisProperties property definition

Five, summary

1. After Spring Boot is started, a large number of configuration classes will be loaded. These configuration classes are defined in the META-INF/spring.factories of the spring-boot-autoconfigure module.

2. If you need to use automatic configuration that is not defined by spring-boot-autoconfigure, you need to separately quote its dependent modules in the pom, such as introducing mybatis-spring-boot-starter to rely on mybatis

3. The role of xxxxAutoConfigurartion automatic configuration class is to add components to the container

4. The role of xxxxProperties is to encapsulate the relevant properties in the configuration file, that is, the properties that can be configured in the configuration file

5. When adding components to the automatic configuration class in the container, certain properties will be obtained from the Properties class. We can specify the values ​​of these properties in the configuration file

Guess you like

Origin blog.csdn.net/ywlmsm1224811/article/details/103815959