本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。
SpringBoot 源码分析
本文基于 SpringBoot 2.3.4.STABLE 版本撰写
1) 依赖管理
2) Maven parent 依赖引入
2.1) spring-boot-starter-parent 分析
一个最基本的 SpringBoot 工程中, 会在 pom.xml
中引入一个 parent 依赖: spring-boot-starter-parent
, 观察此依赖包内容如下:
- spring-boot-starter-parent-2.3.4.RELEASE.pom
- 只有一个
pom
文件, 是一个pom
类型的依赖, 打开:spring-boot-starter-parent-2.3.4.RELEASE.pom
后可以发现引入了一些 Plugin, 没有啥有用的信息. - 翻到头部找找有无父依赖, 发现其还有一个父依赖
spring-boot-dependencies
, 此依赖便是spring-boot-starter-parent
的核心实现:<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.4.RELEASE</version> </parent>
2.1.1) spring-boot-dependencies 分析
老样子先来研究一下目录结构:
- spring-boot-dependencies-2.3.4.RELEASE.pom
发现也是一个 pom
依赖, 打开 spring-boot-dependencies-2.3.4.RELEASE.pom
后一幕明了了, 结构大致如下:
<!-->1. 首先定义了一些 properties 的版本号</-->
<properties>
<activemq.version>5.15.13</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.82</appengine-sdk.version>
<!-->.......</-->
<!-->.......</-->
</properties>
<!-->2. 接着是基于 properties 中的版本号, 利用 maven-dependency-management 管理相应包的依赖版本</-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-amqp</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-blueprint</artifactId>
<version>${activemq.version}</version>
</dependency>
<!-->.......</-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<!-->.......</-->
</dependencies>
</dependencyManagement>
<!-->3. 最后利用 maven-plugin-management 管理插件依赖的版本</-->
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>${build-helper-maven-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>${exec-maven-plugin.version}</version>
</plugin>
<!-->.......</-->
<!-->.......</-->
</pluginManagement>
</build>
总结: spring-boot-starter-parent
中, 利用 maven 的依赖管理, 管理了一些常用包以及 starter 的依赖版本号, 起到了版本号统一管理的作用, 版本号可以提供给 SpringBoot 的其他 starter使用, 起到了让我们引入一些基础依赖或 starter 时, 不需要手动填写版本号的作用.
2.2) spring-boot-starter-web 分析
上一节讲到 spring-boot-starter-parent
主要起到统一管理常用包的一些版本号, 我们拿最常用的依赖包 spring-boot-starter-web
来接着分析一下, 找到包目录中文件如下:
- spring-boot-starter-web-2.3.4.RELEASE.jar
- spring-boot-starter-web-2.3.4.RELEASE.pom
jar
包中只有一些 License 文件, 打开 pom
文件康康带进了啥依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
观察发现带入了以下几种传递依赖:
- SpringBoot 基础库
- Spring json 相关库
- Tomcat
- SpringMVC
总结: spring-boot-starter-web
将 WEB 相关的依赖做了一个打包, 简化项目中 pom
文件的长度, 增强可读性.
3) SpringBoot 自动配置
从一个最基础的启动类开始讲起吧, 本节先讲讲 @SpringBootApplication
注解, 其注解最主要的作用时自动配置, 或者叫自动装配:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.run(args);
}
}
将不重要或基础的代码移除后, @SpringBootApplication
中代码大致如下, 将分为三个小节讲解:
// 注解作用域等
@SpringBootConfiguration // 3.1
@EnableAutoConfiguration // 3.2
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// 注解字段属性
}
3.1) @SpringBootConfiguration
在当前版本中, 此注解只是为我们的启动类, 加上了 @Configuration
注解, 标记为 SpringFramework 的配置类, 代码:
@Configuration
public @interface SpringBootConfiguration {
// ...
}
3.2) @EnableAutoConfiguration
@EnableAutoConfiguration
中的代码就是重头戏了, SpringBoot 自动配置主要就是基于此注解实现, 来康康代码, 两句注解将通过 3.2.1, 3.2.2 两小节讲解:
@AutoConfigurationPackage // 3.2.1
@Import(AutoConfigurationImportSelector.class) // 3.2.2
public @interface EnableAutoConfiguration {
// ...
}
3.2.1) @AutoConfigurationPackage
我们先来康康代码:
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
// ...
}
代码量很少, @Import 注解可以将类导入 Spring 容器, 我们接着看看这个内部类 Registrar:
// 实现了 ImportBeanDefinitionRegistrar, 代表此类使用个性化加载, 并不将 Registrar 本身注入容器
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 1. new PackageImports(metadata).getPackageNames().toArray(new String[0])
// 返回了当前启动类所在的包的包名
// 2. 接着将 registry Bean 注册表, 和上一步获取的包名传入 register 方法
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
// determineImports 方法在 SpringBoot 启动过程中没有调用, 国内外也基本查不到相关作用, 就不讲了
// 官方 Doc 里注释此方法返回一个需要注入到容器的对象数组
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
我们接着来康康 register
方法:
// 1. org.springframework.boot.autoconfigure.AutoConfigurationPackages
private static final String BEAN = AutoConfigurationPackages.class.getName();
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
// 2. Spring 容器中是否包含 AutoConfigurationPackages
if (registry.containsBeanDefinition(BEAN)) { // 启动过程中并不包含, 继续走 else
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
// 3. 定义一个标准 Bean, 相当于需要注入容器的 Bean 的包装类
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
// 4. 定义需要注入容器的类
beanDefinition.setBeanClass(BasePackages.class);
// 5. 根据其第 1 个构造方法, 传入启动类所在包名作为构造器参数, 后续会根据此构造方法反射构建 Bean
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 4. 表明此 Bean 为 Spring 后台注册而非人为注册
// 5. 注入容器, name 为 org.springframework.boot.autoconfigure.AutoConfigurationPackages
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
总结: 很多人以为 register
这个方法会扫描启动类所有子包, 并将 @Component
等注解类标注的类加入容器, 实际上只是将启动类所在包名的包装类, 加入了容器中, BeanName 为: org.springframework.boot.autoconfigure.AutoConfigurationPackages
是的,
@Import(AutoConfigurationPackages.Registrar.class)
注解几乎啥也没做
3.2.1) @Import(AutoConfigurationImportSelector.class)
熟悉的 @Import
, 我们进入 Selector
康康:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// ...
}
- 发现此类实现的是
DeferredImportSelector
, 它也是一个个性化加载接口, 实现它会在所有的@Configuration
注解类处理完才执行导入操作. - 接着实现了其他四个接口:
BeanClassLoaderAware
,ResourceLoaderAware
,BeanFactoryAware
,EnvironmentAware
, 从这四个接口实现的方法会比selectImports
方法(DeferredImportSelector)先执行. - 最后还有一个
Ordered
, 实现了其getOrder
方法:public int getOrder() { return Ordered.LOWEST_PRECEDENCE - 1; }
, 这里也应证了第 1 点.
我们先来康康从第 2 点中实现下来的方法吧:
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
发现没啥信息, 很基础的实现, 没做啥特殊操作, 我们接着康康从 Selector
中实现的方法:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 1. 默认为 True, 可以通过在 SpringBoot 配置文件中将 `spring.boot.enableautoconfiguration=false` 关闭自动装配
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
先进入 getAutoConfigurationEntry
康康:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 1. 默认为 True, 可以通过在 SpringBoot 配置文件中将 `spring.boot.enableautoconfiguration=false` 关闭自动装配
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 2. 获取 @EnableAutoConfiguration 注解中的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 3.3 中讲解
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
代码行数过多, 3.3 中详细讲解:
3.3) getAutoConfigurationEntry
- getCandidateConfigurations
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
我们进入方法内部康康:protected List
getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 加载 spring.factories 中, key 为 EnableAutoConfiguration 全类名的 value 们 List 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; } protected Class> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } 阅读过 SpringBoot 启动源码的童鞋可能对这行代码比较熟悉, 这里不深入讲了, 稍微来看看
loadFactoryNames
的源码:public static List
loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); // 拿到 EnableAutoConfiguration 的全类名 // 从 spring.factories 中读取 key 为 EnableAutoConfiguration 的全类名的值 return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } 最后返回一个自动配置类的 List 集合.
- removeDuplicates
configurations = removeDuplicates(configurations);
进入方法内部看看:protected final
List removeDuplicates(List list) { return new ArrayList<>(new LinkedHashSet<>(list)); } 这就比较简单了 =.=, 用一个 HashSet 对 List 做了一个去重, 然后重新转成一个 List 集合,
removeDuplicates
中主要是对我们的自动配置类做一个去重工作. - getExclusions
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
上面说到,annotationMetadata
是我们启动类的一些元数据信息, attributes 是@EnableAutoConfiguration
注解中的属性, 我们进入getExclusions
方法康康:protected Set
getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { Set excluded = new LinkedHashSet<>(); // 去重集合 // 获取 @EnableAutoConfiguration 中的 exclude 属性 excluded.addAll(asList(attributes, "exclude")); // 获取 @EnableAutoConfiguration 中的 excludeName 属性 excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); // 获取 SpringBoot 配置文件中的 spring.autoconfigure.exclude 配置的排除类, 这里不深入讲解了 excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; } 最后返回一个去重后自动配置类的排除类集合, 我们接着往下看.
- checkExcludeClasses
checkExcludedClasses(configurations, exclusions);
private void checkExcludedClasses(List
configurations, Set exclusions) { List invalidExcludes = new ArrayList<>(exclusions.size()); for (String exclusion : exclusions) { // 1. 排除类如果存在, 且从 spring.factories 中读取的配置类数组中**不包含** if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) { invalidExcludes.add(exclusion); // 2. 塞入 invalidExcludes 异常排除类集合 } } // 3. 如有异常排除类进入 handleInvalidExcludes 方法抛出一个异常, 这里应该会终止 SpringBoot 的启动 if (!invalidExcludes.isEmpty()) { handleInvalidExcludes(invalidExcludes); // 不深入讲了 } } - removeALl
configurations.removeAll(exclusions);
从自动配置类集合中, 移除掉我们配置的排除类. -
getConfigurationClassFilter
configurations = getConfigurationClassFilter().filter(configurations);
先来看看getConfigurationClassFilter()
方法private ConfigurationClassFilter getConfigurationClassFilter() { if (this.configurationClassFilter == null) { // 1. 获取 spring.factories 中的过滤器 List
filters = getAutoConfigurationImportFilters(); for (AutoConfigurationImportFilter filter : filters) { invokeAwareMethods(filter); // 2. 遍历过滤器赋值注入一些环境参数 } // 3. 将过滤器赋值到局部变量 this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters); } return this.configurationClassFilter; } 过滤器的初始化我们分三步来讲解:
- 我们先来康康
getAutoConfigurationImportFilters
的过程protected List
getAutoConfigurationImportFilters() { // 还是熟悉的 loadFactories, 略过不讲了 // 从 spring.factories 中读取 key 为 AutoConfigurationImportFilter.class 的过滤器 return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); } - 然后是执行过滤器
invokeAwareMethods(filter);
, 康康代码:private void invokeAwareMethods(Object instance) { if (instance instanceof Aware) { if (instance instanceof BeanClassLoaderAware) { ((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader); } if (instance instanceof BeanFactoryAware) { ((BeanFactoryAware) instance).setBeanFactory(this.beanFactory); } if (instance instanceof EnvironmentAware) { ((EnvironmentAware) instance).setEnvironment(this.environment); } if (instance instanceof ResourceLoaderAware) { ((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader); } } }
发现只是判断过滤器实现了哪些接口, 注入了一些基本环境参数, 我们回到上一步代码接着往下看看
new ConfigurationClassFilter
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
ConfigurationClassFilter(ClassLoader classLoader, List
filters) { this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader); this.filters = filters; }
发现只是将
filters
集合包装了一下, 还执行了一个loadMetadata
方法:protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties"; static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); // 1. 调用了下方的重载方法 } static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { try { // 2. 加载 spring-autoconfigure-metadata.properties 文件 Enumeration
urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path); // 3. 将 metadata 文件转成 Properties 格式对象 Properties properties = new Properties(); while (urls.hasMoreElements()) { properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement()))); } return loadMetadata(properties); // 4. 再次调用重载方法 } catch (IOException ex) { throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex); } } static AutoConfigurationMetadata loadMetadata(Properties properties) { // 5. 最后创建了一个 porperties 的包装类返回 return new PropertiesAutoConfigurationMetadata(properties); } private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata { private final Properties properties; PropertiesAutoConfigurationMetadata(Properties properties) { this.properties = properties; } // ... } 稍微总结一下过滤器的初始化
getConfigurationClassFilter
, 将spring.factories
中的 filter, 和spring-autoconfigure-metadata.properties
中的每一行数据读取了出来, 包装成了一个对象. - 我们先来康康
- filter
configurations = getConfigurationClassFilter().filter(configurations);
那我们接着康康filter
这个方法:List
filter(List configurations) { long startTime = System.nanoTime(); String[] candidates = StringUtils.toStringArray(configurations); // 1. 将自动配置类转成 String 数组 boolean skipped = false; for (AutoConfigurationImportFilter filter : this.filters) { // 2. 遍历每一个 filter // 3. 将每个 filter 中的 match 方法, 判断自动配置类是否满足启动条件 // filter 是一个过滤器, metadata 则是过滤器过滤自动配置类需要依赖的数据 // 结合过滤器和 metadata 则可以得出符合条件的自动配置类 boolean[] match = filter.match(candidates, this.autoConfigurationMetadata); // 4. 返回自动配置类是否满足条件的 bool 数组 for (int i = 0; i < match.length; i++) { if (!match[i]) { candidates[i] = null; // 5. 将不满足自动配置条件的自动配置类删除 skipped = true; } } } if (!skipped) { return configurations; } // 6. 最后将满足条件的自动配置类返回 List 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; } 看完简介, 我们结合
spring.factories
和spring-autoconfigure-metadata.properties
接着分析一下 3 和 4 之间的代码流程
factories
和metadata
中的自动配置类数据起到一一对应, 从 factories 中读取出的配置类, 在 metadata 中会有一条匹配规则:org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration= org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=com.rabbitmq.client.Channel,org.springframework.amqp.rabbit.core.RabbitTemplaten org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$MessagingTemplateConfiguration= org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$MessagingTemplateConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.core.RabbitMessagingTemplate
比如我需要找
RabbitAutoConfiguration
自动配置类的启动条件, 会从metadata
中读取出这一行:org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=com.rabbitmq.client.Channel,org.springframework.amqp.rabbit.core.RabbitTemplaten
匹配规则大概格式这样: 自动配置类全类名.匹配条件=匹配条件参数
所以,RabbitAutoConfiguraion
需要满足以下条件才加载:ClassPath
中包含com.rabbitmq.client.Channel,org.springframework.amqp.rabbit.core.RabbitTemplaten
这两个类.
匹配条件不止一种, 一共如下:- ConditionalOnBean: Spring 容器中是否包含 Bean
- ConditionalOnClass: ClassPath 中是否包含 Bean
- ConditionalOnCloudPlatform: 是否运行在指定的云平台上
- ConditionalOnExpression: SpEL 表达式结果为 true 时
- ConditionalOnJava: 是否满足指定的 java 版本
- ConditionalOnJndi: 满足指定的 Jndi
- ConditionalOnMissingBean: Spring 容器中不存在某些 Bean 时生效
- ConditionalOnMissingClass: ClassPath 中不存在某些 Class 时生效
- ConditionalOnNotWebApplication: 非 Web 环境下 SpringBoot 运行时生效
- ConditionalOnProperty: SpringBoot 参数设置值相等生效
- ConditionalOnResource: 指定的文件存在时生效
- ConditionalOnSingleCandidate: 当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
- ConditionalOnWarDeployment: 传统 WAR 包部署时生效
- ConditionalOnWebApplication: SpringBoot 以 Web type 运行时生效
深入 match 的代码就不讲了, 太过底层繁杂, 略过.
- fireAutoConfigurationImportEvents
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) { // 1. 从 spring.factories 中读取 key 为 AutoConfigurationImportListener 的监听器集合 List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners(); if (!listeners.isEmpty()) { // 2. 将已过滤的自动配置类集合, 和排除类集合(没错,第3次了)构建一个 Spring 事件 AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions); for (AutoConfigurationImportListener listener : listeners) { invokeAwareMethods(listener); // 3. 监听器环境参数注入 listener.onAutoConfigurationImportEvent(event); // 4. 让监听器执行事件, 没有广播 } } }
- return
最后返回自动配置类和排除类集合的包装类.return new AutoConfigurationEntry(configurations, exclusions);
最终向 Spring 返回了过滤后自动配置类的 String 数组, 交由 Spring 执行自动配置类.
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
SpringBoot 自动配置/装配的源码大概就这样写完了.
4) 总结
- Maven 相关
- parent 中定义了基础包和官方
starter
的版本号 - 一个
starter
中传递依赖了很多子包
- parent 中定义了基础包和官方
- @SpringApplication
@SpringBootConfiguration
: 传递注解@Configuration
@EnableAutoConfiguration
:@AutoConfigurationPackage
: 将启动类所在包名包装成BasePackages
, 加入DI
容器中.@Import(AutoConfigurationImportSelector.class)
:- 从
spring.factories
中加载key
为EnableAutoConfiguration
的自动配置类 String 集合. - 对自动配置类 String 集合去重, 排除.
- 从
spring.factories
中加载key
为AutoConfigurationImportFilter
的过滤器. - 结合过滤器, 和
META-INF
下的metadata
中的条件数据, 对自动配置类做一个去重. - 将过滤后的自动配置类交由 Spring 容器执行.
@ScanComponent
基于上一步中得到的BasePackage
, 扫描启动类路径下的所有组件加入容器.