Springboot Starter 自动配置(Auto-configuration) 源码走读
如何自定义一个starter组件
官方文档
spring-boot-reference-2.4.0 Creating Your Own Auto-configuration
使用场景
如果你在开发共享库的公司工作,或者你在开发开源或商业库,那么你可能希望开发自己的自动配置。自动配置类可以捆绑在外部jar中,并且仍然由Spring Boot加载管理。
自动配置可以关联到一个“启动器”,该启动器提供自动配置代码以及你将与之一起使用的典型库。
组件结构
典型的 Spring Boot Starter 包含用于自动配置实现的模块(
autoconfigure module
)和提供组件所需要的一切依赖的启动模块(starter module
)。
启动模块实际上可认为是一个空的jar,只有一个pom文件,它的唯一目的是提供组件所需的依赖项。
如果自动配置相对简单,并且没有可选特性,那么可以在启动器中合并这两个模块。
组件命名
为了与spring官方项目命名(spring-boot-×××/spring-boot-×××-starter)区分开来
- 自动配置模块命名
×××-spring-boot
- 启动模块命名
×××-spring-boot-starter
实际案例
第三方使用组件时,只需要引入一个starter依赖,即直接配置使用,如引入mybatis:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
mybatis本身不包含jdbc驱动,需要手动引入,除此之外,其他所有依赖在mybatis-spring-boot-starter包中已经明确引入。
自定义组件
自动配置模块-定位自动配置候选对象
springboot会自动检测所有依赖的jar中是否存在META-INF/spring.factories
文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
注意
自动配置必须以这种方式加载。确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。此外,自动配置类不应该启用组件扫描来查找其他组件。应该使用特定的@Imports。
这是spring提供的规范!规范!规范!
,如果不按照规范进行组件编码,则会使得项目结构很乱,并且出现很多“莫名其妙”的bug。
如:你从公司项目中抽离了一个公共组件dynamic-thread-pool
,但是因为组件和项目的包路径都是pers.ergou
,项目启动的时候指定了扫描包路径pers.ergou
,会同时扫描dynamic-thread-pool
组件,此时,即使组件的自动配置错误失效,组件也可能正常加载到spring中,并且不影响使用。以后其他项目再次使用该公共组件时,如果项目包路径非pers.ergou
,就会出现组件没有正常加载的bug。
实例代码
新建maven项目 demo-spring-boot-starter
项目结构
添加pom.xml依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pers.ergou</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
DemoProperties 配置属性类
package pers.ergou.autoconfigure;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author ergou
* @since 2023/9/18 18:33
*/
@ConfigurationProperties(prefix = "demo-spring-boot-starter")
public class DemoProperties {
private boolean enable = false;
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
DemoAutoConfiguration 自动配置类
package pers.ergou.autoconfigure;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author ergou
* @since 2023/9/18 18:31
*/
@Configuration
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfiguration {
// 手动提供bean,注册到上下文中,实现组件功能
// @Bean
// public Xxx xxx() {
// return new Xxx();
// }
// 这里可以根据需求实现各种各样的自定义功能
}
添加配置文件META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
pers.ergou.autoconfigure.DemoAutoConfiguration
这就是一个简单的starter项目,打包该项目,引入到其他项目中,项目会自动按照配置加载DemoAutoConfiguration配置,并且按条件注册配置类中定义的bean。
源码走读
@EnableAutoConfiguration
,@Configuration
,@Import
等注解都是在工厂后置处理器ConfigurationClassPostProcessor
中进行处理。
调用链路
ConfigurationClassPostProcessor
类注释
用于@Configuration类的引导处理。
使用时默认注册,也可以像使用任何其他BeanFactoryPostProcessor一样手动声明。
这个后处理器是按优先级排序的(实现PriorityOrdered接口),因为在@Configuration类中声明的任何Bean方法在任何其他BeanFactoryPostProcessor执行之前都要注册相应的Bean定义,这一点很重要。
源码
package org.springframework.context.annotation;
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
// 从注册中心的配置类派生更多的bean定义。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
// Build and validate a configuration model based on the registry of Configuration classes.
// 构建和验证基于配置类注册表的configuration模型。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 获取已注册的bd
// SpringApplication#run -> SpringApplication#prepareContext 方法中
// load(context, getAllSources().toArray(new Object[0]));
// 将main函数所在类的bd注册到了上下文中
String[] candidateNames = registry.getBeanDefinitionNames();
// candidateNames中,除了主函数类bd外,还有几个默认注册的bp,bfbp和事件监听工厂,如下
// ConfigurationClassPostProcessor 处理@Configuration的bfbp
// AutowiredAnnotationProcessor 处理@Autowired的bp
// RequiredAnnotationProcessor 处理@Required的bp
// CommonAnnotationProcessor 处理@Resource的bp
// EventListenerProcessor 处理注册侦听器,将EventListener注释方法注册为单个ApplicationListener实例。
// EventListenerFactory 侦听器工厂
// CachingMetadataReaderFactory 处元数据缓存
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// isFullConfigurationClass:
// 通过检查checkConfigurationClassCandidate的元数据标记,确定给定的bean定义是否指示完整的@Configuration类。
// 判断是否是full configuration,带有@Configuration注解的类,那么这个类叫做full configuration
// isLiteConfigurationClass:
// 通过检查checkConfigurationClassCandidate的元数据标记,确定给定的bean定义是否指示一个轻量版的@Configuration类。
// 判断是否是lite configuration,带@Component,@ComponentScan,@Import,@ImportResource,@Bean 5个注解中的任一个,那么这个类叫做lite configuration
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 检查给定的bean定义是否是配置类(或在配置/组件类中声明的嵌套组件类,也要自动注册)的候选对象,并相应地对其进行标记。
// 注意注意注意:
// 上面都是检测bd中的attribute属性,checkConfigurationClassCandidate方法会真正判断注解并设置attribute值(如果符合条件)
// key为org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass
// @Configuration value设置为full
// @Component,@ComponentScan,@Import,@ImportResource,@Bean value设置为lite
// 此外,还会获取@Order注解设置attribute
// key=org.springframework.context.annotation.ConfigurationClassPostProcessor.order
// value=@Order指定的数值
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 上面的类中,如果不手动注册配置类,则默认只会存在主类符合条件
// @SpringBootApplication包含@Configuration,@ComponentScan,@Import
// 所以主类是一个FullConfigurationClass
// Return immediately if no @Configuration classes were found
// 如果没有找到@Configuration类,立即返回
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
// 按先前确定的@Order值排序(如果配置类有进行@Order配置)
// 直接使用上面设置的attribute属性排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
// 检测通过封闭应用程序上下文提供的任何自定义bean名称生成策略
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
// 假如环境变量不存在,新建
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
// 解析每个@Configuration类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 需要解析的配置类bd
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 解析完成的配置类
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// ************************************
// ********* 自动配置的核心实现 *******
// ************************************
// candidates 为配置类bd集合
// ConfigurationClassParser#parse源码走读下面有,这里说一下大概步骤:
// 1、将bd封装为配置类ConfigurationClass;
// 2、校验注解元数据 @Conditional,判断该配置类是否需要加载;
// 3、递归解析配置类 (首先递归地处理任何成员(嵌套)类)
// 3.1、处理配置类的 @PropertySource 注解
// 直接将解析到的source扩展到上下文环境中
// 3.2、处理配置类的 @ComponentScan 注解
// 直接执行指定包路径扫描,如果扫描到的bd属于配置类,则从【1】开始执行
// 3.3、processImports 处理配置类的 @Import 注解
// 首先收集所有@Import指定的类,然后分为三种情况:
// 1) ImportSelector接口实现类,如果属于子接口 DeferredImportSelector的实现类,
// 则存储起来,否则,直接执行ImportSelector#selectImports,
// 结果集importClassNames重新从【3.3】开始处理。
// 2) ImportBeanDefinitionRegistrar接口实现类,
// 保存(ConfigurationClass$importBeanDefinitionRegistrars)。
// 3) 其他类,当做普通配置类处理,重新从【2】开始处理。
// 3.4、处理配置类的 @ImportResource 注解,
// 保存(ConfigurationClass$importedResources)
// 3.5、处理单独的 @Bean 方法,保存(ConfigurationClass$beanMethods)
// 3.6、处理接口的默认方法,保存(ConfigurationClass$beanMethods)
// 4、处理【3.3】@Import 中收集的DeferredImportSelector实现类集合
// 即执行AutoConfigurationImportSelector#selectImports(源码详细看下文)
// 执行结果是配置类全限定类名集合,将结果从【3.1】开始处理。
parser.parse(candidates);
// 验证每个ConfigurationClass对象。
// ConfigurationClass对象必须非final
// ConfigurationClass对象的Bean方法,必须为静态或可重写(以适应CGLIB)的
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
// 阅读模型并根据其内容创建bean定义
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 注册bd
// 方法parser.parse(candidates)中,收集的ImportBeanDefinitionRegistrar等实现
// 收集了
// ConfigurationClass$importBeanDefinitionRegistrars
// ConfigurationClass$importedResources
// ConfigurationClass$beanMethods
// loadBeanDefinitions方法实现真正的bd加载注册
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
// 将ImportRegistry注册为bean以支持ImportAware @Configuration类
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
// 清除外部提供的MetadataReaderFactory中的缓存;
// 这是一个无操作的共享缓存,因为它将被ApplicationContext清除。
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
// 省略
}
ConfigurationClassParser
类注释
解析Configuration类定义,填充ConfigurationClass对象的集合(解析单个Configuration类可能会产生任意数量的ConfigurationClass对象,因为一个Configuration类可能会使用import注释导入另一个配置类)。
这个类有助于将解析Configuration类的结构的关注点与基于该模型的内容注册BeanDefinition对象的关注点分离开来(@ComponentScan注释除外,它需要立即注册)。
这个基于ASM的实现避免了反射和急于类加载,以便在Spring ApplicationContext中有效地与惰性类加载进行互操作。
源码
ConfigurationClassParser#parse
parse
package org.springframework.context.annotation;
class ConfigurationClassParser {
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
// 下面的三个parse方法,入参不同,但是都是将入参封装成一个ConfigurationClass对象,
// 然后调用processConfigurationClass(ConfigurationClass configClass)方法
// 主类使用AnnotatedBeanDefinitionReader#registerBean(java.lang.Class<?>)
// 进行bd注册,为AnnotatedGenericBeanDefinition
// (继承GenericBeanDefinition/实现AnnotatedBeanDefinition)
try {
// 带有注解元数据的bd
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
// 具体的、成熟的BeanDefinition类的基类,分解出GenericBeanDefinition、RootBeanDefinition和ChildBeanDefinition的常见属性。
// 自动连接常量与AutowireCapableBeanFactory接口中定义的常量匹配。
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
// 普通类
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 上面的parse方法中执行了@Import方法的解析,
// 但对于@Import的类为DeferredImportSelector的情况,并没有直接执行,
// 而是将其实例化(用DeferredImportSelectorHolder封装),
// 并添加到ConfigurationClassParser$deferredImportSelectors集合中。
// 该方法就是用来处理DeferredImportSelector类的
// AutoConfigurationImportSelector也是DeferredImportSelector的子类之一,
// 所以自动配置其实实在该方法中实现
processDeferredImportSelectors();
}
processConfigurationClass
ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 根据@Conditional注解确定是否应该跳过某项。
// 校验所有@ConditionalOnXxxx的注解,判断是否应该解析该配置类
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
// isImported:
// 返回这个配置类是通过@Import注册的还是由于嵌套在另一个配置类中而自动注册的。
// 如果configClass是通过@Import注册
if (configClass.isImported()) {
if (existingClass.isImported()) {
// 假如两个配置类非@Import导入的,
// 则将给定配置类中的import-by声明合并到现有的这个配置类中。
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
// 否则忽略新导入的配置类;现有的非导入类将覆盖它。
return;
}
// 如果configClass是嵌套在另一个配置类中而自动注册的
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
// 找到显式bean定义,可能替换了导入。
// 我们把旧的拿掉,换上新的吧。
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
// 递归地处理配置类及其超类层次结构。
// 获取 SourceClass 对象
// asSourceClass 为工厂方法:
// 通过 ConfigurationClass 获取 ConfigurationClassParser.SourceClass
// 通过 Class 获取 ConfigurationClassParser.SourceClass
// 通过 class name 获取 ConfigurationClassParser.SourceClass
SourceClass sourceClass = asSourceClass(configClass);
do {
// 通过从源类中读取注解、成员和方法,处理并构建一个完整的ConfigurationClass。
// 当发现相关源时,可以多次调用此方法。
// 真正处理@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean和接口默认方法
//
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
// 缓存配置类
this.configurationClasses.put(configClass, configClass);
}
doProcessConfigurationClass
ConfigurationClassParser#doProcessConfigurationClass
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
// 首先递归地处理任何成员(嵌套)类
// 注册恰好是配置类本身的成员(嵌套)类。
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
// 处理任何@PropertySource注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
// 处理给定的@PropertySource注解元数据。
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// 处理任何@ComponentScan 注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
// 配置类使用注解@ComponentScan -> 立即执行扫描
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
// 检查扫描的定义集以查找任何进一步的配置类,并在需要时递归地进行解析
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 如果扫描到的bd为配置类,直接进行解析(递归parse)
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//***********************************
//********** 重点 *************
//***********************************
// 开放闭合设计原则
// @Import有三种情况:
// 1、候选类是一个ImportSelector
// -> 委托它来确定导入
// 如果为DeferredImportSelector,保存到deferredImportSelectors(如果不为null)
// 这里是自动配置的核心实现入口
// @EnableAutoConfiguration 的 @Import 类为 AutoConfigurationImportSelector.class
// 2、候选类是一个ImportBeanDefinitionRegistrar
// -> 委托给它来注册额外的bean定义
// 3、候选类不是ImportSelector或ImportBeanDefinitionRegistrar
// ->将其作为@Configuration类处理
// Process any @Import annotations
// 处理任何@Import 注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
// 处理任何@ImportResource 注解
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
// 处理任何@Bean注解
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
// 处理接口的默认方法
processInterfaces(configClass, sourceClass);
// Process superclass, if any
// 如果存在父类,则处理父类
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
// 找到超类,返回其注释元数据并递归
// 调用该方法的上层方法
/**
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
} while (sourceClass != null);
*/
// 直接返回父类则会进行循环处理
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
// 没有超类->处理完成
return null;
}
processDeferredImportSelectors
ConfigurationClassParser#processDeferredImportSelectors
该方法会对DeferredImportSelector子类的进行解析处理。
DeterminableImports
目前只有两个实现类:
AutoConfigurationImportSelector
处理@EnableAutoConfiguration
注解。
使用@EnableAutoConfiguration
时会扫描整个类路径下,包括依赖引入的jar包所有的自动配置类(被注解了@Configuration的类)和'META-INF/spring.factories'
文件,尝试进行自动配置。ImportAutoConfigurationImportSelector
处理@ImportAutoConfiguration
注解。
@ImportAutoConfiguration
是@Import
的增强,限制了它使用的特定范围,只运行在你注解中提供的配置类。同样会进行全局扫描,但只会加载指定的配置类。@ImportAutoConfiguration({配置类.class})
private void processDeferredImportSelectors() {
// 获取在parse方法中解析到的DeferredImportSelector集合
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
if (deferredImports == null) {
return;
}
// 排序:PriorityOrdered子类 > Ordered子类 > 其他(默认为Ordered.LOWEST_PRECEDENCE)
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 当使用@EnableAutoConfiguration时,直接指定了 @Import(AutoConfigurationImportSelector.class)
// DeferredImportSelector
// 为 AutoConfigurationImportSelector
// DeferredImportSelector.Group
// 为 AutoConfigurationImportSelector.AutoConfigurationGroup
// DeferredImportSelectorHolder 延迟导入选择器holder,字段如下
// - ConfigurationClass configurationClass 配置类
// - DeferredImportSelector importSelector 延迟导入选择器
// **作用:保存配置类及其对应的导入选择器
// DeferredImportSelector.Group 接口,用于对来自不同导入选择器的结果进行分组。
// 可以理解为DeferredImportSelector的执行器
// 方法如下:
// - process(AnnotationMetadata metadata, DeferredImportSelector selector);
// 使用指定的DeferredImportSelector处理导入的@Configuration类的AnnotationMetadata。
// - Iterable<Entry> selectImports();
// 返回应该为此组导入的类的条目。
// - Entry 内部类,包含AnnotationMetadata metadata 和 String importClassName
// 保存导入的Configuration类的AnnotationMetadata和要导入的类名的条目。
// AutoConfigurationImportSelector.AutoConfigurationGroup 静态内部类
// DeferredImportSelector.Group 接口的实现类,
// 用于执行AutoConfigurationImportSelector.selectImports方法
// DeferredImportSelectorGrouping 为内部类,延迟导入选择器分组 字段如下
// - DeferredImportSelector.Group group 选择器分组
// - List<DeferredImportSelectorHolder> deferredImports 分组包含的选择器holder
// selector分组
Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
// 返回一个特定的导入组,如果不需要分组,则返回null。
// 返回AutoConfigurationImportSelector.AutoConfigurationGroup.class
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
// 创建DeferredImportSelectorGrouping对象,不存在则新建
// key为AutoConfigurationGroup.class或deferredImport
DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 存储deferredImport到DeferredImportSelectorGrouping对象中
grouping.add(deferredImport);
// 设置key=元数据,val=配置类
configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
// 遍历分组
for (DeferredImportSelectorGrouping grouping : groupings.values()) {
// grouping.getImports() 方法,
// 通过持有的group,对deferredImports遍历执行process方法
// 即执行AutoConfigurationImportSelector.AutoConfigurationGroup#process方法
// 最后真正执行的是AutoConfigurationImportSelector#selectImports方法
// 这里就是自动装配的实现逻辑。
// AutoConfigurationImportSelector#selectImports会返回解析到的配置类(META-INF/spring.factories)的全限定名
// 即grouping.getImports()实际上可以认为是解析到的所有配置类全限定名集合
// Map<String, AnnotationMetadata> key为配置类全限定名,AnnotationMetadata元数据都是一样的
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = configurationClasses.get(entry.getMetadata());
try {
// asSourceClasses(className) 通过全限定类型加载来,生成SourceClass对象
// 直接处理配置类,即SourceClass对象
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
// 省略...
}
AutoConfigurationImportSelector
类注释
DeferredImportSelector 处理自动配置。如果需要
@EnableAutoConfiguration
的自定义变体,这个类也可以被子类化。
在上面源码走读过程中可知,选择器分组AutoConfigurationImportSelector.AutoConfigurationGroup#process
内部实际上就是执行了AutoConfigurationImportSelector#selectImports
方法,该方法会返回所有依赖jar以及本身的'META-INF/spring.factories'
中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
的所有全限定类名组成的一个集合。
源码
package org.springframework.boot.autoconfigure;
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断是否需要自动装配
// getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true)
// 配置spring.boot.enableautoconfiguration是否为false
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 加载autoconfigure项目中的"META-INF/spring-autoconfigure-metadata.properties"属性文件(自动配置元数据)
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 从注解元数据返回适当的注解属性。默认情况下,此方法将返回getAnnotationClass()的属性。
// 获取@EnableAutoConfiguration注解的属性(exclude和excludeName)
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 从所有依赖jar和本身的'META-INF/spring.factories'文件中,获取
// key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration
// 的全限定类名集合
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 去除重复的全限定类名
configurations = removeDuplicates(configurations);
// 获取注解属性指定的排除配置类全限定名
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检测指定的排除全限定类名是否符合排除规范
// 类可加载 && 指定的排除全限定类名存在于configurations中
// 否则抛出异常 IllegalStateException
// The following classes could not be excluded
// because they are not auto-configuration classes:XXX
checkExcludedClasses(configurations, exclusions);
// 去除指定排除的配置类
configurations.removeAll(exclusions);
// AutoConfigurationImportFilter接口:
// 设计目的是允许在读取自动配置类的字节码之前快速删除它们
// AutoConfigurationImportFilter默认实现类为OnClassCondition
// OnClassCondition的作用:
// 主要是针对配置类上的注解@ConditionalOnClass, @ConditionalOnMissingClass
// 从所有依赖jar和本身的'META-INF/spring.factories'文件中,获取
// key为org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的全限定类名集合
// 通过全限定类名,加载并实例化AutoConfigurationImportFilter的实现类。
// 遍历对象集合
// - 执行Aware接口进行组件织入 invokeAwareMethods(filter)
// - 对configurations进行过滤处理 filter.match
configurations = filter(configurations, autoConfigurationMetadata);
// 从所有依赖jar和本身的'META-INF/spring.factories'文件中,获取
// key为org.springframework.boot.autoconfigure.AutoConfigurationImportListener的全限定类名集合
// 通过全限定类名,加载并实例化AutoConfigurationImportListener的实现类。
// 遍历对象集合:
// - 执行Aware接口进行组件织入 invokeAwareMethods(listener)
// - 处理自动配置导入事件(AutoConfigurationImportEvent)
// AutoConfigurationImportListener#onAutoConfigurationImportEvent
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回结果
return StringUtils.toStringArray(configurations);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// getSpringFactoriesLoaderFactoryClass()
// -> org.springframework.boot.autoconfigure.EnableAutoConfiguration
// 获取所有依赖jar包中'META-INF/spring.factories'
// key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration
// 的全限定类名集合
// SpringFactoriesLoader.loadFactoryNames([key值], [类加载器])
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;
}
}
SpringFactoriesLoader
类注释
框架内部使用的通用工厂加载机制。
SpringFactoriesLoader从多个JAR文件的类路径“META-INF”下的spring.factories文件中,加载并实例化给定类型的工厂。
源码
package org.springframework.core.io.support;
public abstract class SpringFactoriesLoader {
// 寻找工厂的地点。可以存在于多个JAR文件中。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// spring.factories文件的结果缓存
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
// 使用指定类加载器,从"META-INF/spring.factories"文件中加载全路径工厂类的实现类。
// 参数-factoryClass:指定类,获取spring.factories文件中,key为指定类的全路径类名的所有全路径类名
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 先充缓存获取指定类加载器的缓存,如果存在,则代表已经进行过加载,直接返回
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 获取"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 = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
// 缓存资源
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
// 省略
}