spring 自动配置(中) 自动配置原理
@EnableAutoConfiguration作用原理
参考:
先看springboot2.0自动注入文件spring.factories如何加载详解 AutoConfigurationImportSelector.java:
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
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);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
调用了getCandidateConfigurations:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
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;
}
然后又调用了
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
我们看到
loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
这句话。loadSpringFactories(classLoader)
的返回值是Map<String, List<String>>
,它分析所有包下的META-INF/spring.factories
,将其中配置的k-v对合并。
getOrDefault(factoryClassName, Collections.emptyList());
中,factoryClassName
的值是org.springframework.boot.autoconfigure.EnableAutoConfiguration
(参考文章已经分析过为什么)。
所以合并起来,这句话的意思就是,读取所有包下的META-INF/spring.factories
,将其中配置的k-v对合并,再读取key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
的value数组,将这个数组返回。这也正是loadFactoryNames
所做的事情了。
之后理解getAutoConfigurationEntry
这个方法:
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
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);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
大概就是configurations = removeDuplicates(configurations);
去除重复的配置类,configurations.removeAll(exclusions);
去除要除外的配置类,最后封装一下,返回结果。
我们再看到外层的AutoConfigurationImportSelector.selectImports:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
中最后一句话:
StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
通过autoConfigurationEntry.getConfigurations()
获取要配置的类,再转换成String数组,包含要导入的配置。之后由框架加载。
Springboot 对@Import注解的处理过程
阅读Springboot - @Import 详解 的 Springboot 对@Import注解的处理过程 和Spring 工具类 ConfigurationClassParser 分析得到配置类在ConfigurationClassParser.parse中处理配置类
springboot处理@Import的分析:
- springboot初始化的普通context(非web) 是AnnotationConfigApplicationContext, 在初始化的时候会初始化两个工具类, AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 分别用来从 annotation driven 的配置和xml的配置中读取beanDefinition并向context注册。
- 在初始化 AnnotatedBeanDefinitionReader 的时候, 会向BeanFactory注册一个ConfigurationClassPostProcessor 用来处理所有的基于annotation的bean, 这个ConfigurationClassPostProcessor 是 BeanFactoryPostProcessor 的一个实现
- springboot启动时,在AbstractApplicationContext -> refresh() -> invokeBeanFactoryPostProcessors(beanFactory) 方法中调用注册到它上边的所有的BeanFactoryPostProcessor,其中就包括ConfigurationClassPostProcessor。
- 在上一步中,ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry回调函数会调用,然后postProcessBeanDefinitionRegistry->processConfigBeanDefinitions->parser.parse(candidates);让ConfigurationClassParser处理所有配置类
- parse函数根据Bean定义的不同类型,走不同的分支。但无论哪种情况,最终都会调用processConfigurationClass->doProcessConfigurationClass。然后在此处理各种配置上的注解。
另外,parse方法内,在处理完所有配置类后,调用
this.deferredImportSelectorHandler.process();
,处理DeferredImportSelector的实现类 - doProcessConfigurationClass->processImports中,对于普通ImportSelector会调用
selectImports
,对于DeferredImportSelector会先加入List中,在this.deferredImportSelectorHandler.process();
中回调其process
方法。
配博客原文的一张图帮助理解:
另外,由于spring版本问题,图中的processDeferredImportSelector
应改为this.deferredImportSelectorHandler.process();
。
剩下的看引入的博客就好。
结论
- @EnableAutoConfiguration通过 @Import(AutoConfigurationImportSelector.class)来作用。
- 在框架加载时,会处理@Import注解(上文已经说了springboot怎么处理@Import的了。),调用AutoConfigurationImportSelector.selectImports方法,把每个包内的META-INF/spring.factories读取,并把org.springframework.boot.autoconfigure.EnableAutoConfiguration的自动配置类都读取。
- AutoConfigurationImportSelector.selectImports本身只是读取值,将要加载的自动配置类数组返回,而并不负责加载。返回该数组后,框架就会将其加载。
Mybatis的自动加载
了解了原理,我们看到mybatis-autoconfigure包下的spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
其中,MybatisAutoConfiguration是一个@Configuration,定义了一些默认的Bean。 所以,该文件通过让MybatisAutoConfiguration自动加载,引入了一些默认的Bean,比如SqlSessionFactory、SqlSessionTemplate等。
spring-boot-configuration-processor作用
spring-boot-configuration-processor的作用认为是用来引入@PropertySource的。 也有别的博客说是用来使@ConfigurationProperties生效的。 但我发现不导入spring-boot-configuration-processor也能使用这两个注解。所以我也搞不懂spring-boot-configuration-processor是做什么的。
@ConfigurationProperties与@PropertySource共同作用
SpringBoot标签之@ConfigurationProperties、@PropertySource注解的使用
- 当获取主配置文件中属性值时,只需@ConfigurationProperties(prefix = "person")注解来修饰某类,其作用是告诉springBoot,此类中的属性将与默认的全局配置文件中对应属性一一绑定。属性名必须是application.yml或application.properties。【prefix = "person"】表示与配置文件中哪个层级的属性进行绑定。
- 当一些属性不想配置到主配置文件,需自定义一个配置文件,需通过@PropertySource注解指定此配置文件路径。并用@ConfigurationProperties(prefix = "xxx")注解指定自定义配置文件中哪个层级属性需绑定。 @ConfigurationProperties(prefix = "person") @PropertySource(value ={"classpath:person.properties"}) @Validated public class Person {...}
- 配置文件的位置:srcmainresourcesapplication.yml
META-INF/spring-autoconfigure-metadata.properties
这个文件是如何被加载的:
- AutoConfigurationImportSelector是一个DeferredImportSelector,在spring加载BeanFactoryPostProcessor->加载@Imports时,调用回调函数DeferredImportSelector.process。
- 在process内,有代码如下: AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
- 首先调用getAutoConfigurationMetadata()。该方法解析所有的
META-INF/spring-autoconfigure-metadata.properties
,以key-value对的形式存储在AutoConfigurationMetadata中。 - 然后调用getAutoConfigurationEntry,传入了上一步的参数。该方法调用的
configurations = filter(configurations, autoConfigurationMetadata);
用到了上一步传入的参数
- 首先调用getAutoConfigurationMetadata()。该方法解析所有的
- filter方法。调用了
filter.match(candidates, autoConfigurationMetadata);
,看到match的接口注释,就知道该方法返回一个bool数组,代表candidates中哪些是Configuration是满足加载条件的。 至于match又是怎么做的,再往深我就没探究了。
这个文件毕竟是spring-boot-autoconfigure-processor自动生成的,用于spring加快加载速度用的,我们只要会用就好,不必过于关注其原理。
ImportSelector DeferredImportSelector
ImportSelector DeferredImportSelector区别 DeferredImportSelector会在其它ImportSelector加载完成后才加载。 DeferredImportSelector的载入:
// ConfigurationClassParser.processImports
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(
configClass, (DeferredImportSelector) selector);
}
DeferredImportSelector的回调:
ConfigurationClassParser.parse->this.deferredImportSelectorHandler.process();
->handler.processGroupImports();
。
其中,
- grouping.getImports()调用了回调函数
process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)
和selectImports()
- grouping.getImports().forEach内调用了processImports加载
- 而selectImports(AnnotationMetadata annotationMetadata)并没有被调用。
@ConditionalOnClass
@Configuration
@ConditionalOnClass({Billy.class})
public class VanConfig {
// ...
}
作用在@Configuration上,当Billy.class存在于classpath时,才加载VanConfig。 conditional系列
- 大数据之Yarn——Capacity调度器概念以及配置
- 移动端web开发,click touch tap区别
- 大数据学习之路(持续更新中...)
- 如何养成良好的c++编程习惯(1)——内存管理
- 使用jOrgChart插件实现组织架构图的展示
- Spark源码分析 之 Driver和Excutor是怎么跑起来的?(2.2.0版本)
- webpack入门——webpack的安装与使用
- Portal-Basic Java Web应用开发框架V3.0正式发布(源码、实例及文档)
- ELK5.0安装教程
- 替换空格
- JavaScript实现段落文本高亮
- 原生js实现简单移动端轮播图
- html5页面实现点击复制功能
- python生成式
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Jmeter 常用函数(14)- 详解 __strLen
- Jmeter 常用函数(15)- 详解 __StringFromFile
- Jmeter 常用函数(16)- 详解 __split
- Jmeter 常用函数(18)- 详解 __isDefined
- Jmeter 常用函数(19)- 详解 __BeanShell
- PyTorch 60分钟入门系列之训练分类器
- Jmeter 常用函数(20)- 详解 __counter
- Jmeter 常用函数(21)- 详解 __char
- Jmeter 常用函数(22)- 详解 __intSum
- Jmeter 常用函数(23)- 详解 __longSum
- Jmeter 常用函数(24)- 详解 __digest
- you-dont-know-websocket
- Cent os 7之KVM虚拟化基础管理
- 常用 Maven 命令介绍
- Linux下diff命令用法详解