SpringBoot自动配置源码深入刨析讲解
作者:天黑请闭眼丶风 发布时间:2022-04-03 14:38:06
自动配置底层源码分析
本次springboot源码来自2.6.6版本。
@EnableAutoConfiguration源码解析
在springboot中,当我们引入某个依赖,就可以直接使用依赖里面的类进行自动注入,不需要像ssm框架那样在xml文件中配置各种bean,然后进行关联。像这样我们称之为自动配置。那么自动配置到底配了什么?
SpringBoot中的自动配置,更多的是配置各种Bean,同时对于端口号这些配置,一些特定属性SpringBoot也是会提供一种默认值的,也相当于一种自动配置。
那SpringBoot是如何自动的帮助我们来配置这些Bean的呢?并且如果某些Bean程序员自己也配置了,那SpringBoot是如何进行选择的呢?
在springboot启动类中有@SpringBootApplication注解,该注解包含了@EnableAutoConfiguration
而@EnableAutoConfiguration的作用就是导入AutoConfigurationImportSelector.class这个类。在spring中的配置类源码分析中,分析过@Import导入的类会当成配置类来解析,并且如果这个配置类是实现了DeferredImportSelector接口,就会调用selectImports方法。这部分属于spring源码的知识不在赘述。
有上述类关系图中可以看出,会调用AutoConfigurationImportSelector的selectImports方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 会在所有@Configuration都解析完了之后才执行
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取自动配置类(spring.factories中所导入的)
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
而selectImports的核心代码在于getAutoConfigurationEntry(annotationMetadata)。接下来一步步分析这个方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取@EnableAutoConfiguration的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取spring.factories中所有的AutoConfiguration
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重(也就是按类名去重)
configurations = removeDuplicates(configurations);
// 获取需要排除的AutoConfiguration,可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude来配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 排除
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 获取spring.factories中的AutoConfigurationImportFilter对AutoConfiguration进行过滤
// 默认会拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition
// 这三个会去判断上面的AutoConfiguration是否符合它们自身所要求的条件,不符合的会过滤掉,表示不会进行解析了
// 会利用spring-autoconfigure-metadata.properties中的配置来进行过滤
// spring-autoconfigure-metadata.properties文件中的内容是利用Java中的AbstractProcessor技术在编译时生成出来的
configurations = getConfigurationClassFilter().filter(configurations);
// configurations表示合格的,exclusions表示被排除的,把它们记录在ConditionEvaluationReportAutoConfigurationImportListener中
fireAutoConfigurationImportEvents(configurations, exclusions);
// 最后返回的AutoConfiguration都是符合条件的
return new AutoConfigurationEntry(configurations, exclusions);
}
首先执行 AnnotationAttributes attributes = getAttributes(annotationMetadata);拿到@EnableAutoConfiguration的属性封装成AnnotationAttributes 。List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
加载自动配置类。看看源码是怎么获取的
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//核心方法 传入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;
}
//返回EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
//获取类加载器
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//这个name就是EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
//这部分代码具体的去加载自动配置类
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());这部分代码如下图,
通过类加载去加载资源,其中红色部分的静态变量就是 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
。也就是说类加载器去META-INF/spring.factories里面的资源
而.getOrDefault(factoryTypeName, Collections.emptyList());
这部分就是根据factoryTypeName也就是EnableAutoConfiguration。获取EnableAutoConfiguration的值封装成List<String>
到此就获取到了所有自动配置类。那么List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
这个方法就结束了。接着执行configurations = removeDuplicates(configurations);
这部分主要用去重
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
接着执行Set<String> exclusions = getExclusions(annotationMetadata, attributes);
这个方法主要是把需要排除的配置类的类名放入到集合当中。
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
//获取EnableAutoConfiguration注解的exclude属性的值 添加到排除集合当中
excluded.addAll(asList(attributes, "exclude"));
//获取EnableAutoConfiguration注解的excludeName属性的值 添加到排除集合当中
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
//从配置文件中获取spring.autoconfigure.exclude 的值 添加到排除集合中
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
往下执行checkExcludedClasses(configurations, exclusions);
和configurations.removeAll(exclusions);
从之前获取到的自动配置类的类名中排除掉那些需要被排除了类名。
接着执行configurations = getConfigurationClassFilter().filter(configurations);
。将排除后的自动配置类的名称作为入参,这部分代码是提前判断一些条件进行过滤掉不需要加载的自动配置类
private ConfigurationClassFilter getConfigurationClassFilter() {
if (this.configurationClassFilter == null) {
//获取自动配置类的过滤器
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
//将所有过滤器封装成 ConfigurationClassFilter
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
//底层从 META-INF/spring.factories中加载 AutoConfigurationImportFilter的值
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
上面获取到的过滤器就是这部分
获取到所有过滤器后通过filter(configurations);
进行过滤
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
//把自动配置类的名称封装成数组
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
// 逐个利用AutoConfigurationImportFilter来判断所有的自动配置类的条件是否匹配,匹配结果存在match数组中
// 先利用OnBeanCondition进行过滤
// 再利用OnClassCondition进行过滤
// 再利用OnWebApplicationCondition进行过滤
for (AutoConfigurationImportFilter filter : this.filters) {
//把过滤的结果 放入到boolean的数组中
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
//如果匹配失败 排除掉该自动配置类
candidates[i] = null;
skipped = true;
}
}
}
// 全部都匹配
if (!skipped) {
return configurations;
}
// 把匹配的记录在result集合中,最后返回
List<String> result = new ArrayList<>(candidates.length);
for (String candidate : candidates) {
if (candidate != null) {
result.add(candidate);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return result;
}
}
过滤完成后执行fireAutoConfigurationImportEvents(configurations, exclusions);
这部分不重要 ,可以看成就是记录一个日志,哪些成功的哪些被排除的。
最后执行return new AutoConfigurationEntry(configurations, exclusions);
这部分代码把 可以加载的自动配置类 放入到一个集合,把排除的放入到另一个集合
AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
this.configurations = new ArrayList<>(configurations);
this.exclusions = new HashSet<>(exclusions);
}
public List<String> getConfigurations() {
return this.configurations;
}
public Set<String> getExclusions() {
return this.exclusions;
}
到此所有需要加载的自动配置类都找到了。然后再回到一开始的selectImports
方法这个方法最后返回StringUtils.toStringArray(autoConfigurationEntry.getConfigurations())
。返回所有符合自动配置类的全类名。根据@Import功能会继续将selectImports
返回的类名,当成配置类去加载。那么每个自动配置类就会加载到springboot当中。
到此springboot自动配置功能就结束了。至于加载自动配置类加载过程中,根据条件注解去匹配是否能够加载,下一篇在分析。
来源:https://blog.csdn.net/admin522043032/article/details/126637549
猜你喜欢
- java HashMap多层嵌套package chapter12;import java.util.HashMap;public clas
- 本文实例为大家分享了Java实现医院管理系统的具体代码,供大家参考,具体内容如下1.开发工具NetBeans8.2Mysql5.7mysql
- 前言:线程池是一个非常重要的知识点,也是池化技术的一个典型应用,相信很多人都有使用线程池的经历,但是对于线程池的实现原理大家都了解吗?本篇文
- spring-boot-starter-actuator提供服务健康检查和暴露内置的url接口。spring-cloud-starter-c
- 写在前面:spring 应该对于每个从事java开发的大兄弟们来说应该都不陌生的,作为一个从业两年多的小开发仔,个人觉得,每天都在面对spr
- 前言之前在SpringBoot项目中一直使用的是SpringFox提供的Swagger库,上了下官网发现已经有接近两年没出新版本了!前几天升
- trim中prefix与suffix等标签用法1.prefix 前缀增加的内容2.suffix 后缀增加的内容3.prefixOverrid
- 当我们需要与 NIO Channel 进行交互时, 我们就需要使用到 NIO Buffer, 即数据从 Buffer读取到 Channel
- 1、一个示例回顾Future一些业务场景我们需要使用多线程异步执行任务,加快任务执行速度。JDK5新增了Future接口,用于描述一个异步计
- 创建类第一步新建一个java类QSV,构造函数传入需要解析的文件名称。public class QSV {private RandomAcc
- MyBatis-PlusMyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改
- 使用IDEA开发微服务项目,需要启动多个微服务,可以开启IDEA的Run DashBoard窗口,需要对IDEA中指定工程的父工程进行配置进
- 本文主要介绍了Spring Security OAuth2 实现登录互踢的示例代码,分享给大家,具体如下:背景说明一个账号只能一处登录,类似
- @ApiImplicitParam作用在方法上,表示单独的请求参数参数name:参数名。value:参数的具体意义,作用。required:
- C++11 引入一个全新的线程库,包含启动和管理线程的工具,提供了同步(互斥、锁和原子变量)的方法,我将试图为你介绍这个全新的线
- springboot项目还是ssm等java常用框架都会有这样的问题,解决办法通用问题场景前端发送Post请求,前端返回400 Bad Re
- MD5加密简介哈希算法又称散列算法,是将任何数据转换成固定长度的算法的统称。 从本质上讲,MD5也是一种哈希算法,其输出是生成12
- AspectJ简介它不属于spring;AspectJ是一个AOP的框架;定义了AOP语法;有一个专门的编译器用来生成遵守Java字节编码规
- 相信大家都经常使用String 的split方法,但是大家有没有遇到下面的这种情况:大家想想下面的代码执行结果是什么public stati
- 基本语法C#,又名Csharp,天朝喜欢叫C井。C#是一种面向对象的编程语言。在面向对象的程序设计方法中,程序有各种相互交互的对象组成。相同