一、前沿
在 Spring Boot 启动流程 文章中我们了解了 Spring Boot 的启动流程,其中在刷新上下文过程中加载了所有的自动化配置类
而 Spring Boot 的自动化配置加载分为了两个步骤:
1)、首先在 Spring Boot 初始化阶段通过 SpringFactoriesLoader 将所有 MATA-INF/spring.factories 文件中的所有配置内容以一对多的类名集合存入到缓存中
spring-boot-autoconfigure 模块中的 MATA-INF/spring.factories 文件 EnableAutoConfiguration 对应的类集合如下:
加载到缓存中的类集合如下所示:
2)、然后在刷新上下文过程中,通过 SpringFactoriesLoader 的 loadFactoryNames 方法获取自动配置类的集合,最后通过反射获取这些类的类对象、构造方法进而生成类实例
创建自动化配置类过程如下:
二、自动化配置流程图
为了更好地理解自动化配置,通过一张图来说明,如下图所示:
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 类结构如下:
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过程如下图所示:
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,如下图所示:
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 属性,如下图所示:
右边红框中的所有属性全部来自 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 类中获取某些属性,我们可以在配置文件中指定这些属性的值