一、前沿

在 Spring Boot 启动流程 文章中我们了解了 Spring Boot 的启动流程,其中在刷新上下文过程中加载了所有的自动化配置类

而 Spring Boot 的自动化配置加载分为了两个步骤:

1)、首先在 Spring Boot 初始化阶段通过 SpringFactoriesLoader 将所有 MATA-INF/spring.factories 文件中的所有配置内容以一对多的类名集合存入到缓存中

spring-boot-autoconfigure 模块中的  MATA-INF/spring.factories 文件 EnableAutoConfiguration 对应的类集合如下:

spring boot排除自动配置 spring boot自动配置流程_spring

加载到缓存中的类集合如下所示:

spring boot排除自动配置 spring boot自动配置流程_redis_02

2)、然后在刷新上下文过程中,通过 SpringFactoriesLoader 的 loadFactoryNames 方法获取自动配置类的集合,最后通过反射获取这些类的类对象、构造方法进而生成类实例

创建自动化配置类过程如下:

spring boot排除自动配置 spring boot自动配置流程_Spring Boot 2.x 源码_03

二、自动化配置流程图

为了更好地理解自动化配置,通过一张图来说明,如下图所示:

spring boot排除自动配置 spring boot自动配置流程_Spring Boot 2.x 源码_04

mybatis-spring-boot-starter 等组件的 META-INF 目录下均含有 spring.factories 文件,而 SpringFactoriesLoader 会扫描所有 spring.factories 文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean

三、自动化配置源码

开启 Spring Boot 自动化配置功能的是 @EnableAutoConfiguration 注解,下面我们就从 @EnableAutoConfiguration 注解开始讲起

3.1 EnableAutoConfiguration

@EnableAutoConfiguration 功能是开启 SpringBoot 自动配置,源码如下:

@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 {};

}

从 @EnableAutoConfiguration 注解源码可知 AutoConfigurationImportSelector 类是实现自动配置的核心所在

3.2  AutoConfigurationImportSelector

AutoConfigurationImportSelector 类结构如下:

spring boot排除自动配置 spring boot自动配置流程_spring boot排除自动配置_05

AutoConfigurationImportSelector 中的核心方法 getAutoConfigurationEntry 源码如下:

// 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;
	}

看完 AutoConfigurationImportSelector 实现的核心方法之后,就是弄清楚哪里调用的 AutoConfigurationImportSelector 的方法呢?

答案肯定是在刷新上下文时调用的,具体过程看自动配置类创建过程

3.3  自动配置类创建过程

自动配置类创建经过以下两个过程:

1)、首先通过 AbstractApplicationContext  的 invokeBeanFactoryPostProcessors 方法将配置的自动配置类转换为 BeanDefinition 并注册到 BeanFactory 中,如果自动配置类需要生成代理类,则会使用CGLIB动态代理直接生成对应的代理类

2)、然后通过 AbstractApplicationContext  的 finishBeanFactoryInitialization 方法将 BeanDefinition 实例化成对应的 Bean

3.3.1  转为 BeanDefinition

具体的入口在 AbstractApplicationContext 的 invokeBeanFactoryPostProcessors 方法,debug过程如下图所示:

spring boot排除自动配置 spring boot自动配置流程_spring_06

debug具体栈信息:

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)

经过 AbstractApplicationContext 的 invokeBeanFactoryPostProcessors 方法之后,将所有 spring.factories 文件中自动配置类解析成了 BeanDefinition,如下图所示:

spring boot排除自动配置 spring boot自动配置流程_自动配置_07

3.3.2  BeanDefinition 生成 Bean 实例

生成了 BeanDefinition 之后,就是将BeanDefinition 生成对应的 Bean 实例,而对于 Bean 的创建我们已经在 Spring 源码之 Bean 加载 分析过了,这里不在赘述了

四、自动配置类示例分析

我们以 RedisAutoConfiguration 为例,通过源码理解其原理

RedisAutoConfiguration 源码如下:

// 表明是一个配置类,其所有被 @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;
	}

}

所有在配置文件中能配置的属性都是在 xxxProperties 类中定义好的,配置文件能配置哪些属性就可以参照某个功能对应的这个属性类,例如 RedisAutoConfiguration 源码中的 @EnableConfigurationProperties(RedisProperties.class) ,RedisProperties 文件中定义的属性就是我们可以在配置文件中配置的属性

RedisProperties 源码如下:

// 所有属性的前缀
@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:password@example.com: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;
}

从 RedisProperties 源码可知,配置文件中可以配置 spring.redis.url、spring.redis.host、spring.redis.password 等属性

配置文件中配置 redis 属性,如下图所示:

spring boot排除自动配置 spring boot自动配置流程_Spring Boot 2.x 源码_08

右边红框中的所有属性全部来自 RedisProperties 属性定义

五、总结

1、Spring Boot 启动后会加载大量的配置类,这些配置类都定义在 spring-boot-autoconfigure 模块的 META-INF/spring.factories 中

2、如果需要使用 spring-boot-autoconfigure 没有定义的自动配置的话,需要在 pom 中单独引用其依赖模块,比如引入 mybatis-spring-boot-starter 依赖使用 mybatis

3、xxxxAutoConfigurartion 自动配置类的作用就是给容器中添加组件

4、xxxxProperties 的作用就是封装配置文件中相关属性,即配置文件中可以配置的属性

5、给容器中自动配置类添加组件的时候,会从 Properties 类中获取某些属性,我们可以在配置文件中指定这些属性的值