背景

众所周知,如下即可启动一个最简单的Spring应用。查看@SpringBootApplication注解的源码,发现这个注解上有一个重要的注解@EnableAutoConfiguration,而这个注解就是SpringBoot实现自动装配的基础

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

@EnableAutoConfiguration

EnableAutoConfiguration注解上通过@Import引入了两个类,org.springframework.boot.autoconfigure.AutoConfigurationImportSelectororg.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar。通过@Import标注的类,会在解析@Import所在的配置类时,将标注类引入容器解析,并进行注册。

有众多的组件都是通过在配置类上加@EnableAutoConfiguration注解将组件引入的

  • ImportBeanDefinitionRegistrar实现了org.springframework.context.annotation.ImportBeanDefinitionRegistrarorg.springframework.boot.context.annotation.DeterminableImports

  • AutoConfigurationImportSelector实现了org.springframework.context.annotation.DeferredImportSelector

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
....
} @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
...
}

解析

起始

通过BeanFactoryPostProcessor对需要注册的Bean进行解析。即org.springframework.context.support.AbstractApplicationContext#refresh,在AbstractApplicationContext#invokeBeanFactoryPostProcessors方法调用时,就开始了对服务配置bean的解析,为对象的生成做准备

        @Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
... try {
... invokeBeanFactoryPostProcessors(beanFactory); ...
} catch (BeansException ex) {
...
} finally {
...
}
}
}

具体解析

调用org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors,通过获取到的BeanFactoryPostProcessor实现类对各种配置类进行解析,具体的BeanFactoryPostProcessor解析后面我们在具体分析。

这里有一个很重要的类org.springframework.context.annotation.ConfigurationClassPostProcessor,首先会调用postProcessBeanDefinitionRegistry方法

	// ConfigurationClassPostProcessor类部门源码

	/**
* Derive further bean definitions from the configuration classes in the registry.
*/
@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
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
...
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry); // configCandidates为待解析的Configuration类,如配置了@SpringBootApplication的类
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 开始解析
parser.parse(candidates);
parser.validate(); ...
}
while (!candidates.isEmpty());
...
}

通过源码可知,具体的解析操作是在org.springframework.context.annotation.ConfigurationClassParser类中

public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 将配置类进行解析。以当前配置类为原配置类,解析@PropertySource、@ComponentScan、@Import、@ImportResource、
// @Bean等标注的类或方法,生成对应的
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
...
} // 解析通过@Import引入的配置类,自动配置类的解析也在于此
this.deferredImportSelectorHandler.process();
} public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// grouping.getImports()方法获取到了所有配置的可用自动配置类,然后遍历,以配置类原点又开始一轮解析。自动装配就是在此处
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
// import的解析
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}

通过DeferredImportSelectorGrouping.getImports()方法解析。在org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry方法中开始了autoConfiguration的解析。

	/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 解析@EnableAutoConfiguration注解中的属性exclude、excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 使用SpringFactoriesLoader获取META-INF/spring.properties中配置的EnableAutoConfiguration实现类,获取所有配置的自动装配类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 获取需要排除的自动装配类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//getConfigurationClassFilter()方法就是获取spring.factories中配置的AutoConfigurationImportFilter实现类。然后调用filter //法对自动装配类进行有效性校验
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

再继续看org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.ConfigurationClassFilter#filter

	List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
// autoConfigurationMetadata为通过META-INF/spring-autoconfigure-metadata.properties配置文件的内容
// 使用filter及autoConfigurationMetadata对candidates进行校验
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;
}
...
return result;
}

再继续看match方法,org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#match

	@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
// 抽象方法,不同的filter进行不同的处理。这里会获取每一个自动装配类的条件判断情况
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}

通过match方法,经过多种filter的过滤,返回的就是每一个自动配置类是否可用

结论

  • SpringBoot项目有一个子项目org.springframework.boot:spring-boot-autoconfigure:xx,这个子项目主要就是做自动装配的。SpringBoot提前配置了众多已经实现自动配置功能的配置类(org.springframework.boot.autoconfigure.EnableAutoConfiguration接口的实现类)。当容器启动的时候,通过 SpringFactoriesLoader将配置类加载进容器中
  • 启动中,容器通过BeanFactoryPostProcessor接口解析、修改对象的定义。有一个很重要的配置解析实现类org.springframework.context.annotation.ConfigurationClassPostProcessor,用来解析项目中标注@Configuration 的类
  • 在进行配置类解析时(即解析配置了@SpringBootApplication注解的类),需要经过解析类的 @PropertySource@ComponentScan@Import@ImportResource@Bean、接口默认实现、父类等(org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass)。对于自动装配来说,最重要的就是解析@Import
  • 通过@Import引入了org.springframework.boot.autoconfigure.AutoConfigurationImportSelector,在进行解析@Import引入的配置类时,org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry获取到所有配置的自动装配类(通过META-INF/spring.factories文件配置EnableAutoConfiguration实现类),通过org.springframework.context.annotation.Condition定义过滤器,判断自动装配置是否需要自动装配。默认的过滤器有OnClassConditionOnWebApplicationConditionOnBeanCondition,对应常见的condition注解ConditionalOnClassConditionalOnBean@ConditionalOnWebApplication
  • 通过过滤判断,将需要自动配置的类进行configuration解析,从而将需要配置的类转换成对应的BeanDefinition进行注册

备注

  • SpringBoot将自动装配类及过滤条件通过配置文件的形式放在了META-INF目录下,META-INF/spring.factoriesMETA-INF/spring-autoconfigure-metadata.properties

  • BeanFactoryPostProcessor进行调用时,有两种处理。首先是通过BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry解析更多的BeanDefinition,在这里就包含了所有标注类的扫描解析,自动装配类的解析,自动装配类引入类的解析。在进行BeanFactoryPostProcessor#postProcessBeanFactory调用,进行CGLIB-enhanced配置类。这里最重要的一个类就是org.springframework.context.annotation.ConfigurationClassPostProcessor,以下为此类的继承关系



    )

SpringBoot是如何做到自动装配的的更多相关文章

  1. SpringBoot启动代码和自动装配源码分析

    ​ 随着互联网的快速发展,各种组件层出不穷,需要框架集成的组件越来越多.每一种组件与Spring容器整合需要实现相关代码.SpringMVC框架配置由于太过于繁琐和依赖XML文件:为了方便快速集成第三 ...

  2. SpringBoot SpringApplication底层源码分析与自动装配

    目录 抛出问题 @SpringBootApplication注解剖析 SpringApplication类剖析 第一步:配置SpringBoot Bean来源 第二步 :自动推断SpringBoot的 ...

  3. SpringBoot学习(三)探究Springboot自动装配

    目录 什么是自动装配 何时自动装配 原理分析 注:以下展示的代码springboot的版本为2.0.3版.因源码过长,大家选择展开代码 ㄟ( ▔, ▔ )ㄏ 什么是自动装配 自动装配还是利用了Spri ...

  4. SpringBoot | 2.1 SpringBoot自动装配原理

    @ 目录 前言 1. 引入配置文件与配置绑定 @ImportResource @ConfigurationProperties 1.1 @ConfigurationProperties + @Enab ...

  5. SpringBoot自动装配-源码分析

    1. 简介 通过源码探究SpringBoot的自动装配功能. 2. 核心代码 2.1 启动类 我们都知道SpringBoot项目创建好后,会自动生成一个当前模块的启动类.如下: import org. ...

  6. 深入理解SpringBoot之自动装配

    SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提.其实它并不那么神秘,我在这之前已经写过最基本的实现了,大家可以参考这篇文章.这次主要的议题是,来看看它是怎么样实现的,我们透过源代码 ...

  7. SpringBoot自动装配源码解析

    序:众所周知spring-boot入门容易精通难,说到底spring-boot是对spring已有的各种技术的整合封装,因为封装了所以使用简单,也因为封装了所以越来越多的"拿来主义" ...

  8. springboot自动装配

    Spring Boot自动配置原理 springboot自动装配 springboot配置文件 Spring Boot的出现,得益于“习惯优于配置”的理念,没有繁琐的配置.难以集成的内容(大多数流行第 ...

  9. SpringBoot学习之自动装配

    在前面使用SSM集成时,我们可以使用注解实现无配置化注入,但是这种依赖被进行“人工干预了的”,换句话就是说我们手动进行装配,那么此时还没有达到SpringBoot这种自动装配的效果,那么究竟Sprin ...

随机推荐

  1. SYCOJ#1077、01字符串

    题目-01字符串 (shiyancang.cn) 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 ...

  2. 信不信由你!iPhone6屏幕宽度不一定是375px,iPhone6 Plus屏幕宽度不一定是414px

    看到这个题目你可能不信,引出这个问题的缘由是几次项目中Chrome模拟器和iPhone6真机预览效果不一致. 为什么在Chrome Emulation模拟手机页面和真机预览效果不一致? 以前觉得不外乎 ...

  3. Spring循环依赖原理

    Spring循环依赖的原理解析 1.什么是循环依赖? ​ 我们使用Spring的时候,在一个对象中注入另一个对象,但是另外的一个对象中也包含该对象.如图: 在Student中包含了teacher的一个 ...

  4. php中使用CURL之php curl详解

    curl是个什么东西?简单地说就是,curl是一个库,能让你通过URL和许多不同种的服务器进行勾搭.搭讪和深入交流,并且还支持许多协议.并且人家还说了curl可以支持https认证.http post ...

  5. 洛谷 CF196A 题解

    题目传送门 题目描述: 读入字符串,求该串的最大字典序子序列. 我的思路: 循环判断只要当前字符比后面所有的字符的字典序大,就把这个字符存到另一个字符串中,最后和输入的字符串循环比较如果该字符和输入时 ...

  6. virtual studio发布到gihub

    问题 我们想要发布代码到github或者微软团队服务时候,往往发现没有本地库,所以难以发布. 解决方案 在解决方右击就可以新建git 文件都会出现小锁说明有了记录 文件夹会对应出现 右上角管理连接也会 ...

  7. 推荐召回--基于物品的协同过滤:ItemCF

    目录 1. 前言 2. 原理&计算&改进 3. 总结 1. 前言 说完基于用户的协同过滤后,趁热打铁,我们来说说基于物品的协同过滤:"看了又看","买了又 ...

  8. vue开源项目有点全

    目录 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 UI组件 element ★31142 - 饿了么出品的Vue2的web UI工具套件 Vux ★14104- 基于Vue和 ...

  9. python组合

    目录 一:组合基础使用 二:组合 一:组合基础使用 组合: 就是一个对象拥有一个属性,该属性的值是另一个对象 继承:满足什么是什么的关系,is-a的关系 继承是一把双刃剑,单继承能实现就尽量少的继承, ...

  10. [数据结构]伸展树(Splay)

    #0.0 写在前面 Splay(伸展树)是较为重要的一种平衡树,理解起来也依旧很容易,但是细节是真的多QnQ,学一次忘一次,还是得用博客加深一下理解( #1.0 Splay! #1.1 基本构架 Sp ...