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

SpringBoot自动配置源码深入刨析讲解

SpringBoot自动配置源码深入刨析讲解

而@EnableAutoConfiguration的作用就是导入AutoConfigurationImportSelector.class这个类。在spring中的配置类源码分析中,分析过@Import导入的类会当成配置类来解析,并且如果这个配置类是实现了DeferredImportSelector接口,就会调用selectImports方法。这部分属于spring源码的知识不在赘述。

SpringBoot自动配置源码深入刨析讲解

有上述类关系图中可以看出,会调用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());这部分代码如下图,

SpringBoot自动配置源码深入刨析讲解

通过类加载去加载资源,其中红色部分的静态变量就是 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";。也就是说类加载器去META-INF/spring.factories里面的资源

.getOrDefault(factoryTypeName, Collections.emptyList());这部分就是根据factoryTypeName也就是EnableAutoConfiguration。获取EnableAutoConfiguration的值封装成List<String>

SpringBoot自动配置源码深入刨析讲解

到此就获取到了所有自动配置类。那么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);
}

上面获取到的过滤器就是这部分

SpringBoot自动配置源码深入刨析讲解

获取到所有过滤器后通过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

标签:SpringBoot,自动配置
0
投稿

猜你喜欢

  • java中多线程与线程池的基本使用方法

    2023-06-23 15:24:09
  • C#线程中弹窗的制作方法

    2023-08-14 03:25:32
  • Android 逆向学习详解及实例

    2022-12-26 08:51:50
  • Mybatis中resultMap的Colum和property属性详解

    2023-09-16 11:17:33
  • 从内存地址解析Java的static关键字的作用

    2022-03-18 03:49:26
  • java 文件下载支持中文名称的实例

    2023-03-16 09:02:16
  • Spring的Aware接口实现及执行顺序详解

    2023-03-09 09:50:53
  • Java代理模式实例详解【静态代理与动态代理】

    2023-04-27 08:18:51
  • C#实现创建,删除,查找,配置虚拟目录实例详解

    2022-09-27 06:38:16
  • java中获取json的所有key方法

    2023-10-15 06:15:26
  • Java 数据结构进阶二叉树题集下

    2022-07-11 19:16:18
  • springboot添加https服务器的方法

    2022-08-19 06:14:31
  • 在启动后台 jar包时,使用指定的 application.yml操作

    2023-01-08 20:10:12
  • Java for循环几种写法整理

    2023-09-12 01:25:58
  • SpringBoot配置log4j输出日志的案例讲解

    2023-07-30 18:54:50
  • 三道java新手入门面试题,通往自由的道路--锁+Volatile

    2023-09-04 20:33:42
  • Android ListView组件详解及示例代码

    2022-04-27 07:12:08
  • Bootstrap 下拉菜单.dropdown的具体使用方法

    2023-07-08 19:10:46
  • mybatis查询返回Map<String,Object>类型的讲解

    2022-12-25 02:07:38
  • JavaWeb ServletContext基础与应用详细讲解

    2021-12-20 22:13:10
  • asp之家 软件编程 m.aspxhome.com