springboot启动流程 (3) 自动装配
在SpringBoot中,EnableAutoConfiguration注解用于开启自动装配功能。
本文将详细分析该注解的工作流程。
EnableAutoConfiguration注解
启用SpringBoot自动装配功能,尝试猜测和配置可能需要的组件Bean。
自动装配类通常是根据类路径和定义的Bean来应用的。例如,如果类路径上有tomcat-embedded.jar,那么可能需要一个TomcatServletWebServerFactory(除非已经定义了自己的Servlet WebServerFactory Bean)。
自动装配试图尽可能地智能化,并将随着开发者定义自己的配置而取消自动装配相冲突的配置。开发者可以使用exclude()排除不想使用的配置,也可以通过spring.autoconfig.exclude属性排除这些配置。自动装配总是在用户定义的Bean注册之后应用。
用@EnableAutoConfiguration注解标注的类所在包具有特定的意义,通常用作默认扫描的包。通常建议将@EnableAutoConfiguration(如果没有使用@SpringBootApplication注解)放在根包中,以便可以搜索所有子包和类。
自动装配类是普通的Spring @Configuration类,使用SpringFactoriesLoader机制定位。通常使用@Conditional方式装配,最常用的是@ConditionalOnClass和@ConditionalOnMissingBean注解。
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* 当类路径下没有指定的类时,可以使用这个属性指定排除的类
*/
String[] excludeName() default {};
}
该注解Import了AutoConfigurationImportSelector类,AutoConfigurationImportSelector类实现了DeferredImportSelector接口。
Import注解和DeferredImportSelector接口在之前的"Spring @Import注解源码分析"中详细分析过,此处在介绍它们,只分析AutoConfigurationImportSelector的工作流程。
AutoConfigurationImportSelector类
DeferredImportSelector接口
A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.
Implementations can also extend the org.springframework.core.Ordered interface or use the org.springframework.core.annotation.Order annotation to indicate a precedence against other DeferredImportSelectors.
Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.
AutoConfigurationGroup类
AutoConfigurationImportSelector的getImportGroup方法返回了AutoConfigurationGroup类。
private static class AutoConfigurationGroup implements
DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
// ... 略
@Override
public void process(
AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) {
// AutoConfigurationEntry类使用List保存Configuration类
AutoConfigurationEntry autoConfigurationEntry =
((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
@Override
public Iterable<Entry> selectImports() {
// 查找排除的配置类
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
// 所有配置类
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations)
.flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
// 将排除的配置类移除掉
processedConfigurations.removeAll(allExclusions);
// 排序
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
// ... 略
}
从上面的代码可以看出,查找自动装配类的逻辑在getAutoConfigurationEntry方法中。
getAutoConfigurationEntry方法
从META-INF/spring.factories文件解析EnableAutoConfiguration配置。
META-INF/spring.factories文件示例:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
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);
// 这里的Filter是从META-INF/spring.factories文件解析出来的
configurations = getConfigurationClassFilter().filter(configurations);
// 触发事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(
AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 从META-INF/spring.factories文件查找EnableAutoConfiguration配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}
SpringFactoriesLoader类loadFactoryNames方法
Load the fully qualified class names of factory implementations of the given type from "META-INF/spring.factories", using the given class loader.
public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
// 从类路径下查找META-INF/spring.factories文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 获取properties配置
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName :
StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
// 把配置添加缓存
cache.put(classLoader, result);
return result;
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
springboot启动流程 (3) 自动装配的更多相关文章
- SpringBoot启动流程与自动装配
是什么 Spring Boot基于Spring框架之上的一个微服务架构开发框架大大简化了Spring的开发.因为Spring Boot提供了大量的自动配置.而且它是基于Java配置方式的开发(全注解) ...
- SpringBoot启动代码和自动装配源码分析
随着互联网的快速发展,各种组件层出不穷,需要框架集成的组件越来越多.每一种组件与Spring容器整合需要实现相关代码.SpringMVC框架配置由于太过于繁琐和依赖XML文件:为了方便快速集成第三 ...
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程解析
写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...
- SpringBoot启动流程分析(六):IoC容器依赖注入
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(一):SpringApplication类初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(二):SpringApplication的run方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(四):IoC容器的初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot源码学习3——SpringBoot启动流程
系列文章目录和关于我 一丶前言 在 <SpringBoot源码学习1--SpringBoot自动装配源码解析+Spring如何处理配置类的>中我们学习了SpringBoot自动装配如何实现 ...
随机推荐
- 【UniApp】-uni-app-CompositionAPI传递数据
前言 好,经过上个章节的介绍完毕之后,了解了一下 uni-app-传递数据 那么了解完了uni-app-传递数据之后,这篇文章来给大家介绍一下 uni-app-CompositionAPI传递数据 首 ...
- MySQL运维15-一主一从读写分离
一.读写分离介绍 读写分离,是把数据库的读和写分开操作,以应对不同的数据库服务器.主数据库提供写操作,从数据库提供读操作,这样能有效的减轻单台数据库的压力. 二.一主一从原理 MySQL的主从复制是基 ...
- 腾讯云服务器安装MySQL5.7基于xshell
腾讯云服务器安装MySQL5.7基于xshell 下载MySQL57安装包 wget https://dev.mysql.com/get/mysql57-community-release-el7-1 ...
- Config:Spring Cloud分布式配置组件
Config:Spring Cloud分布式配置组件 问题总结 Config? Config工作原理? Config 的特点? Config+Bus 实现配置的动态刷新? 问题答案 Config Co ...
- DNSlog注入(利用DNSlog平台将SQL盲注变成回显注入)
前言 什么是UNC 什么是DNSlog注入 DNSlog注入的条件 防止DNSlog注入的几个措施 sqli-labs试验 前言 前几天面试的时候,面试官问我知不知道OOB(带外数据). 当时我蒙了, ...
- Reactor 简介
官方的介绍如下: Reactor is a fully non-blocking reactive programming foundation for the JVM, with efficient ...
- MyBatis 源码解析
本文源码解析针对的是 MyBatis 3.4.4 MyBatis 执行流程 第一阶段 MyBatis 在这个阶段获得 Mapper 的动态代理对象,具体逻辑如下图所示: 其中,Configuratio ...
- Javac多模块化编译
转载:原文链接 从SDK9开始,Java支持多模块编译.那么,怎么用javac实现多模块编译呢? 项目介绍 先来看看我们的项目. 首先lib文件夹下是依赖模块,有一个hello模块.hello模块包含 ...
- 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 16.优化交互,实现看到物体时出现交互提示
斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论 概述 本篇文章对应Lecture 18 – Creating Buffs, World Interaction, 73 ...
- CodeForces 1459C 数论 GCD
CodeForces 1459C 数论 GCD 原题链接 题意 首先给出n个数 之后给出m个数,每次问之前的n个数加上当前的这个数之后,总体的gcd是多少,也就是答案需要求出m个总体的gcd 思路 因 ...