Spring Boot启动过程及自动装配原理

简述

Spring Boot相对Spring的优点主要有两个:

  • 起步依赖-会将很多jar包按照功能合并成stater整体进行版本管理和引用,解决Spring集成其他框架时jar版本管理问题
  • 自动装配-引入相关的jar包后SpringBoot会自动注册一些比较关键的bean,并进行默认配置,不用我们进行特殊配置,解决Spring重量级XML配置问题。比如整合Mybatis时的SqlSessionFactory

SpringBoot启动依靠的是带有main方法的启动类,启动类的内容可以分为两个部分一个是启动类上@SpringBootApplication这个注解;第二部分是main方法里的SpringApplication.run(启动类.class,args)方法。下面探讨的内容是基于Spring Boot 2.1.7-RELEASE版本。

Spring Boot自动装配原理(从注解@SpringBootApplication入手)

@SpringBootApplication是个组合注解包含四个元注解和@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan组成,下面逐个分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,Runtime运行时
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
//上面为元注解
@SpringBootConfiguration // 继承了Configuration,表示当前是配置类
@EnableAutoConfiguration // 启动自动配置功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { // 扫描路径设置 启动类所在包及子包下的类
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
} 

@SpringBootConfiguration

@SpringBootConfiguration也是一个组合注解,由元注解和@Configuration构成。@Configuration@Component的一个衍生注解主要作用是标记当前类是个配置类。
@SpringBootConfiguration注解类代码如下:

1
2
3
4
5
6
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

@Configuration注解类代码如下:

1
2
3
4
5
6
7
8
9
10
11
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
}

@EnableAutoConfiguration

@EnableAutoConfiguration也是一个组合注解由元注解和@AutoConfigurationPackage@Import注解构成,Spring中有很多Enable开头的注解,其作用大都是借助@Import来收集并注册特定场景相关的bean。
@EnableAutoConfiguration的主要作用就是借助@Import来收集并注册所有符合自动装配条件的bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//注册BasePackages类到Spring容器,在整合jpa时会用到,非这次讲解的重点
@AutoConfigurationPackage
//将所有符合条件的@Configuration配置都加载到SpringBoot创建的IOC容器中(ApplicationContext)
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

Class<?>[] exclude() default {};

String[] excludeName() default {};
}
@AutoConfigurationPackage

@SpringBootApplication扫描启动类当前包及其子包下面的类是由下文的@ComponentScan注解完成的。非@AutoConfigurationPackage注解完成的。

1
2
3
4
5
6
7
8
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//给容器中导入某个组件类,现在是将Registrar这个组件类导入到容器中
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

Registrar.class实现了ImportBeanDefinitionRegistrar接口会调用registerBeanDefinitions方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}

//获取的是项目主程序启动类所在的目录
//metadata:注解标注的元数据信息
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}

public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
@Import({AutoConfigurationImportSelector.class})

AutoConfigurationImportSelector类是SpringBoot实现自动装配的关键。AutoConfigurationImportSelector实现了DeferredImportSelector(DeferredImportSelector接口, 表示为延迟导入)、BeanClassLoaderAware接口,而DeferredImportSelector又继承了ImportSelector接口。其中selectImports方法会返回一个数组,数组中的类后面都会注册到Spring容器中。

1
2
3
4
5
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
...
}

DeferredImportSelector接口是ImportSelector接口的子接口,该接口会在所有的@Configuration配置类(不包括自动化配置类,即spring.factories文件中的配置类)处理完成后运行。DeferredImportSelector有两个特点:

  • 继承该接口的ImportSelector会在所有@Configuration配置类处理完后运行。这一点是因为在ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>) 方法中直到解析出来其他的候选配置类才会调用this.deferredImportSelectorHandler.process(); 来解析DeferredImportSelector
  • 如果定义了一个以上的DeferredImportSelector则使用Order接口来进行排序。这一点也是在this.deferredImportSelectorHandler.process();中进行了排序调用。

ImportSelector接口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14

/**
*
* @see DeferredImportSelector
* @see Import
* @see ImportBeanDefinitionRegistrar
* @see Configuration
*/
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);

该接口注释说明:实现该接口的selectImport则返回的String[]的 class字符串 类将被注册为@Configuration,即只要实现该接口返回的类将被注入Spring容器。@see ImportBeanDefinitionRegistrar则说明是使用 ImportBeanDefinitionRegistrar的registerBeanDefinitions方法进行注入。

AutoConfigurationImportSelector.AutoConfigurationGroup类对selectImports方法实现

1
2
3
4
5
6
7
8
9
10
11
//AutoConfigurationImportSelector.AutoConfigurationGroup
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
// 返回的就是需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组
// ["com.bobo.pojo.User","com.bobo.pojo.Person", ....]
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

该方法的作用就是要返回需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组。

下面我们重点对getAutoConfigurationEntry(annotationMetadata)进行分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 1. 判断是否开启注解。如未开启,返回空串
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//1、 获取注解的属性信息
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//2、获取候选配置信息,加载的是当前项目的classpath目录下所有的spring.factories文件中的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的信息
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//3、去除重复的配置类,若我们自己写的starter 可能存在重复的
configurations = this.removeDuplicates(configurations);
//4、如果项目中某些自动配置类,我们不希望其自动配置,我们可以通过EnableAutoConfiguration的exclude或excludeName属性进行配置,
// 或者也可以在配置文件里通过配置项“spring.autoconfigure.exclude”进行配置。
//找到不希望自动配置的配置类(根据EnableAutoConfiguration注解的一个exclusions属性)
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
//4.1、校验排除类(exclusions指定的类必须是自动配置类,否则抛出异常)
this.checkExcludedClasses(configurations, exclusions);
//4.2、将需要排除的类从 configurations remove掉
configurations.removeAll(exclusions);
//5、 对所有候选的自动配置类进行筛选,根据项目pom.xml文件中加入的依赖文件筛选出最终符合当前项目运行环境对应的自动配置类
//要判断@Conditional是否满足
//如@ConditionalOnClass({SqlSessionFactory.class,SqlSessionFactoryBean.class})表示需要在类路径中存在SqlSessionFactory.class、SqlSessionFactoryBean.class这两个类才能完成自动注册。
configurations = this.getConfigurationClassFilter().filter(configurations);
//6、将自动配置导入事件通知监听器
//当AutoConfigurationImportSelector过滤完成后会自动加载类路径下Jar包中META-INF/spring.factories文件中AutoConfigurationImportListener的实现类,
//并触发fireAutoConfigurationImportEvents事件。
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}

我们来看一下getCandidateConfigurations(annotationMetadata, attributes)方法是怎么拿到这些自动配置类的。

1
2
3
4
5
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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.java类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();

while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();

while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;

for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}

cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}

通过SpringFactoriesLoader扫描所有的META-INF/spring.factories中所有路径下的类全路径名(key为org.springframework.boot.autoconfigure.EnableAutoConfiguration),并通过反射实例化成一个个的配置类并注入到Spring容器中,从而实现了自动装配。

接着我们看下this.getExclusions(annotationMetadata, attributes);方法的作用。该方法用于排除主类上@SpringBootApplication注解上排除的自动装配的类。比如我们在该注解上排除我们自定义starter的自动装配的类,@SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})(当然也可以用excludeName进行排除),那么在后面的configurations.removeAll(exclusions);方法中将会删除我们的com.demo.starter.config.DemoConfig.class

configurations=filter(configurations,autoConfigurationMetadata);该行代码将会过滤掉不需要装配的类。过滤的逻辑有很多,比如我们常用的@ConditionXXX注解。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
@ConditionalOnBean:容器中有指定的Bean
@ConditionalOnClass:当类路径下有指定的类
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
@ConditionalOnMissingBean:当容器中没有指定Bean的情况下
@ConditionalOnMissingClass:当类路径下没有指定的类
@ConditionalOnNotWebApplication:当前项目不是Web项目
@ConditionalOnProperty:配置文件中指定的属性是否有指定的值
@ConditionalOnResource:类路径下是否有指定的资源
@ConditionalOnSingleCandidate:当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件下

@ComponentScan

默认扫描启动类所在包以及子包路径下全部类

小结

实现自动装配这块最核心的注解就是@EnableAutoConfiguration,其中起作用的子注解为@Import({AutoConfigurationImportSelector.class})。在AutoConfigurationImportSelector.AutoConfigurationGroupselectImports方法中,通过SpringFactoriesLoaderloadFactoryNames()方法扫描到所有META-INF/spring.factories文件中对应key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类全路径名。并通过getExclusions.getConfigurationClassFilter().filter(configurations)进行过滤处理。

决定一个XxxAutoConfiguration是否需要进行装配,由以下几方面控制:

  • @EnableAutoConfiguration参数控制
  • spring.autoconfigure.exclude参数控制
  • spring-autoconfigure-metadata.properties文件中指定的规则控制
  • XxxAutoConfiguration本身的条件控制,如硬编码的@ConditionalOn

那么selectImports返回的类全路径名称数组在何时加载、实例化加入到IOC容器呢?在下面的章节最后部分会讲到。

Spring Boot启动过程(从代码run()入手)

SpringBoot项目的mian函数代码

1
2
3
4
5
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

我们进入run()方法看下,其代码在SpringApplication.class文件。

1
2
3
4
5
6
7
8
//
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}

我们发现其实其首先是创建了SpringApplication的实例,然后调用了SpringApplication的run()方法。

创建SpringApplication实例

接下来我们看下SpringApplication构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//项目启动类 Application.class设置为属性存储起来
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置初始化器(Initializer),最后会调用这些初始化器
//所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器(Listener)
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
this.mainApplicationClass = this.deduceMainApplicationClass();
}

在实例化SpringApplication中设置的初始化器和监听器都是在/META-INF/spring.factories 中获取的。

deduceFromClasspath()-推断应用的类型

我们看下WebApplicationType枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;

private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

private WebApplicationType() {
}

static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;

for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}

return SERVLET;
}
}
}
  • NONE: 应用程序不是web应用,也不应该用web服务器去启动
  • SERVLET: 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
  • REACTIVE: 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。

run方法逻辑

run方法在SpringApplication.class中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public ConfigurableApplicationContext run(String... args) {
// 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// ConfigurableApplicationContext Spring 的上下文
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//1、从META-INF/spring.factories中获取并启动监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();

Collection exceptionReporters;
try {
//创建 ApplicationArguments 对象 初始化默认应用参数类
// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//2、项目运行环境Environment的预配置
// 创建并配置当前SpringBoot应用将要使用的Environment
// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
Banner printedBanner = this.printBanner(environment);
//3、创建Spring容器,初始化应用上下文
context = this.createApplicationContext();
//实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误,也是利用getSpringFactoriesInstances方法来获取配置的异常类名称并实例化所有的异常处理类
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//4、Spring容器前置处理
//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//5、刷新容器
this.refreshContext(context);
//6、Spring容器后置处理
//扩展接口,设计模式中的模板方法,默认为空实现。
// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
this.afterRefresh(context, applicationArguments);
//停止 StopWatch 统计时长
stopWatch.stop();
//打印 Spring Boot 启动的时长日志。
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//7、发出结束执行的事件通知
listeners.started(context);
//8、执行Runners
//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}

try {
//9、发布应用上下文就绪事件
//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
// 这样整个Spring Boot项目就正式启动完成了。
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}

run方法步骤一共分为9步:

  • 获取并启动监听器,监听器也是在spring.factories中获取的。
  • 项目运行环境Environment的预配置
  • 创建Spring容器
  • Spring容器前置处理,这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
  • 刷新容器
  • Spring容器后置处理,扩展接口,设计模式中的模板方法,默认为空实现。
  • 向监听器发出结束执行的事件通知
  • 执行Runners
  • 向监听器发布应用上下文就绪事件
    上述步骤中,最重要的为第4、5步。
第一步:获取并启动监听器

事件机制在Spring是很重要的一部分内容,通过事件机制我们可以监听Spring容器中正在发生的一些事件,同样也可以自定义监听事件。Spring的事件为Bean和Bean之间的消息传递提供支持。当一个对象处理完某种任务后,通知另外的对象进行某些处理,常用的场景有进行某些操作后发送通知,消息、邮件等情况。

1
2
3
4
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

这里也是从META-INF/spring.factories获取指定的监听器实例

可以看到EventPublishingRunListener监听器是Spring容器的启动监听器。

第二步:配置项目运行环境

这里的运行环境包括计算机的环境,Java环境,Spring的运行环境,Spring项目的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//创建并配置相应的环境
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
//根据用户配置,配置 environment系统环境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
//启动相应的监听器,其中一个重要的监听器 ConfigFileApplicationListener 就是加载项目配置文件的监听器。
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}

ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}

该方法中主要完成的工作,首先是创建并按照相应的应用类型配置相应的环境,然后根据用户的配置,配置系统环境,然后启动监听器,并加载系统配置文件。

configureEnvironment(ConfigurableEnvironment environment, String[] args)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
//将main 函数的args封装成 SimpleCommandLinePropertySource 加入环境中。
this.configurePropertySources(environment, args);
// 激活相应的配置文件
this.configureProfiles(environment, args);
}

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}

if (this.addCommandLineProperties && args.length > 0) {
String name = "commandLineArgs";
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
} else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}


protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles();
Set<String> profiles = new LinkedHashSet(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

如我们给项目提供了两个配置文件application-dev.ymlapplication-prod.yml。当我们想要在生产环境使用application-prod.yml配置时,只需要在启动参数增加--spring.profiles.active=prod。这其中就用到了configurePropertySources(environment, args);中将args封装成了SimpleCommandLinePropertySource并加入到了environment中,同时利用configureProfiles(environment, args);根据启动参数激活了相应的配置文件。

listeners.environmentPrepared((ConfigurableEnvironment)environment)

在SpringApplication实例化对象过程中,this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));会获取并加载META-INF/spring.factories中key为org.springframework.context.ApplicationListener下的所有Listener。

environmentPrepared方法中,ConfigFileApplicationListener会从以下几个位置加载配置文件内容

  • classpath:
  • file:./
  • classpath:config/
  • file:./config/:
第三步:创建Spring容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}

return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

以web工程为例,即其上下文为AnnotationConfigServletWebServerApplicationContext,我们先看一下AnnotationConfigServletWebServerApplicationContext的设计。

第四步:Spring容器前置处理(关键)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置容器环境
context.setEnvironment(environment);
//执行容器后置处理
this.postProcessApplicationContext(context);
//执行容器中的 ApplicationContextInitializer 包括spring.factories和通过三种方式自定义的
this.applyInitializers(context);
//触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
listeners.contextPrepared(context);
//记录启动日志
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}

//将main函数中的args参数封装成单例Bean,注册进容器
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
//将 printedBanner 也封装成单例,注册进容器
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}

if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources,在getAllSources()中拿到了我们的启动类
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
this.load(context, sources.toArray(new Object[0]));
//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
listeners.contextLoaded(context);
}

在前置处理中最核心的一步是加载我们的启动类,将启动类注入容器。

第五步:刷新容器(关键)

refresh相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
private void refreshContext(ConfigurableApplicationContext context) {
this.refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}

}

protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext)applicationContext).refresh();
}

public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 第一步 刷新前的预处理
this.prepareRefresh();
// 第二步 1.创建BeanFactory实例,默认实现是DefaultListableBeanFactory
// 2.解析XML中的<bean>为BeanDefition 并注册到 BeanDefitionRegistry
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 第三步 BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加载器等)
this.prepareBeanFactory(beanFactory);

try {
// 第四步 BeanFactory准备工作完成后的后置处理工作,钩子方法,等子类重写
this.postProcessBeanFactory(beanFactory);
// 第五步 实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
// 提前初始化工厂后置处理器bean,并调用postProcessBeanFactory方法
//其中BeanFactoryPostProcessor比较重要的一个ConfigurationClassPostProcessor在这里调用,
//用来遍历BeanDefinitionRegistry中现有的BeanDefinition解析@Import、@Configuration
// 、@ComponentScan等注解将注解覆盖到的类也注册到BeanDefinitionRegistry中
this.invokeBeanFactoryPostProcessors(beanFactory);
// 第六步 注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
this.registerBeanPostProcessors(beanFactory);
// 第七步 初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
this.initMessageSource();
// 第八步 初始化事件派发器
this.initApplicationEventMulticaster();
// 第九步 ⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑,钩子方法
this.onRefresh();
// 第十步 注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
this.registerListeners();
// 第十一步 初始化所有剩下的⾮懒加载的单例bean
//1).初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
//2).填充属性
//3) .如果bean实现了Aware相关接口,则调用Aware接口的实现方法
//4) .调用BeanPostProcessor处理器的前置方法
//5).初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
//6).调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
this.finishBeanFactoryInitialization(beanFactory);
// 第十二步 完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件 (ContextRefreshedEvent)
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}

this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}

}
}
invokeBeanFactoryPostProcessors

invokeBeanFactoryPostProcessors()方法是这期的重点。实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean,就是在这一步解析的@SpringBootApplication这个组合注解,BeanFactoryPostProcessor比较重要的一个ConfigurationClassPostProcessor在这里调用,用来遍历BeanDefinitionRegistry中现有的BeanDefinition解析@Import@Configuration@ComponentScan等注解将注解覆盖到的类也注册到BeanDefinitionRegistry中。

IOC容器初始化过程的三个步骤,也是在invokeBeanFactoryPostProcessors方法中完成。

  • BeanDefinition的Resource定位
  • BeanDefinition的载入
  • 向IoC容器注册BeanDefinition
  • 第一步:Resource定位

在SpringBoot中,我们都知道他的包扫描是从主类所在的包开始扫描的,prepareContext()方法中,会先将主类解析成BeanDefinition,然后在refresh()方法的invokeBeanFactoryPostProcessors()方法中解析主类的BeanDefinition获取basePackage的路径。这样就完成了定位的过程。其次SpringBoot的各种starter是通过SPI扩展机制实现的自动装配,SpringBoot的自动装配同样也是在invokeBeanFactoryPostProcessors()方法中实现的。还有一种情况,在SpringBoot中有很多的@EnableXXX注解,细心点进去看的应该就知道其底层是@Import注解,在invokeBeanFactoryPostProcessors()方法中也实现了对该注解指定的配置类的定位加载。

常规的在SpringBoot中有三种实现定位,第一个是主类所在包的,第二个是SPI扩展机制实现的自动装配(比如各种starter),第三种就是@Import注解指定的类。

  • 第二步:BeanDefinition的载入

在第一步中说了三种Resource的定位情况,定位后紧接着就是BeanDefinition的分别载入。所谓的载入就是通过上面的定位得到的basePackage,SpringBoot会将该路径拼接成:classpath*:org/springframework/boot/demo/**/*.class这样的形式,然后一个叫做PathMatchingResourcePatternResolver的类会将该路径下所有的.class文件都加载进来,然后遍历判断是不是有@Component注解,如果有的话,就是我们要装载的BeanDefinition。

  • 第三个过程:注册BeanDefinition

这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。比如DefaultListableBeanFactory中的beanDefinitionMap属性。

具体看下代码实现过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
...
}
// PostProcessorRegistrationDelegate类
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
...
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
...
}
// PostProcessorRegistrationDelegate类
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
// ConfigurationClassPostProcessor类
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
...
processConfigBeanDefinitions(registry);
}
// ConfigurationClassPostProcessor类
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
...
//Spring Boot项目的话,这里只有一个配置类就是我们的启动类
Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
HashSet alreadyParsed = new HashSet(configCandidates.size());
do {
//将解析出来的配置类@Configuration注解,封装成ConfigurationClass对象放入到parser.configurationClasses变量中
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());
}
//将解析配置类加载到容器中
this.reader.loadBeanDefinitions(configClasses);
...
}
...
}

走到ConfigurationClassParser类的parse()方法这里,包扫描及自动装配入口在这个方法里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public void parse(Set<BeanDefinitionHolder> configCandidates) {
Iterator var2 = configCandidates.iterator();

while(var2.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();
BeanDefinition bd = holder.getBeanDefinition();

try {
// 如果是SpringBoot项目进来的,bd其实就是前面主类封装成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的实现类)
if (bd instanceof AnnotatedBeanDefinition) {
this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {
this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());
} else {
this.parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException var6) {
throw var6;
} catch (Throwable var7) {
throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7);
}
}
//加载默认的配置---》(对springboot项目来说这里就是自动装配的入口了)
this.deferredImportSelectorHandler.process();
}

包扫描方式加载注册Bean
第一步:Resource定位

沿着this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// ConfigurationClassParser类
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
// ConfigurationClassParser类
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
//检查配置类上的@Conditional注解,如果不满足则直接跳过,若是已经导入的,则直接移除
if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}

return;
}

this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
// Recursively process the configuration class and its superclass hierarchy.
//递归地处理配置类及其父类层次结构。
SourceClass sourceClass = asSourceClass(configClass);
do {
//递归处理Bean,如果有父类,递归处理,直到顶层父类
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);

this.configurationClasses.put(configClass, configClass);
}
// ConfigurationClassParser类
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {

// Recursively process any member (nested) classes first
//首先递归处理Component注解的内部类
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
this.processMemberClasses(configClass, sourceClass);
}
// Process any @PropertySource annotations
// 针对 @PropertySource 注解的属性配置处理
AnnotationAttributes importResource;
while(var3.hasNext()) {
importResource = (AnnotationAttributes)var3.next();
if (this.environment instanceof ConfigurableEnvironment) {
this.processPropertySource(importResource);
} else {
this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
}
}

// Process any @ComponentScan annotations
// 根据 @ComponentScan 注解,扫描项目中的Bean(SpringBoot 启动类上有该注解)
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
// 立即执行扫描,(SpringBoot项目为什么是从主类所在的包扫描,这就是关键了)
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();
}
// 检查是否是ConfigurationClass(是否有configuration/component两个注解),如果是,递归查找该类相关联的配置类。
// 所谓相关的配置类,比如@Configuration中的@Bean定义的bean。或者在有@Component注解的类上继续存在@Import注解。
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}

// Process any @Import annotations
//递归处理 @Import 注解(SpringBoot项目中经常用的各种@Enable*** 注解基本都是封装的@Import)
processImports(configClass, sourceClass, getImports(sourceClass), true);

// Process any @ImportResource annotations
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);
}
}

//解析@Bean注解
// Process individual @Bean methods
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
return sourceClass.getSuperClass();
}
}

// No superclass -> processing is complete
return null;
}

上面的doProcessConfigurationClass()方法是SpringBoot的包扫描的入口方法。

for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(...获取主类上的@PropertySource注解,解析该注解并将该注解指定的properties配置文件中的值存储到Spring的Environment中,Environment接口提供方法去读取配置文件中的值,参数是properties文件中定义的key值。

Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 解析主类上的@ComponentScan注解。

parse(bdCand.getBeanClassName(), holder.getBeanName());会进行递归调用,因为当Spring扫描到需要加载的类会进一步判断每一个类是否满足是@Component/@Configuration注解的类,如果满足会递归调用parse()方法,查找其相关的类。

processImports(configClass, sourceClass, getImports(sourceClass), true);通过@Import注解查找到的类同样也会递归查找其相关的类。

我们看下this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());扫描包过程

1
2
3
4
5
6
7
8
9
10
11
12
13
// ComponentScanAnnotationParser类
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
...
// 根据 declaringClass (如果是SpringBoot项目,则参数为主类的全路径名)
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
...
// 根据basePackages扫描类
return scanner.doScan(StringUtils.toStringArray(basePackages));
}

接着往下看return scanner.doScan(StringUtils.toStringArray(basePackages)); Spring是如何进行类扫描的,进入doScan()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ComponentScanAnnotationParser类
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 从指定的包中扫描需要装载的Bean
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//将该 Bean 注册进 IoC容器(beanDefinitionMap)
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}

doScan中有两个比较重要的方法,Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 从basePackage中扫描类并解析成BeanDefinition,拿到所有符合条件的类后在registerBeanDefinition(definitionHolder, this.registry); 将该类注册进IoC容器。也就是说在这个方法中完成了IoC容器初始化过程的第二、三步,BeanDefinition的载入和BeanDefinition的注册。

第二步:BeanDefinition载入-findCandidateComponents

我们看下findCandidateComponents(basePackage);代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 // ClassPathScanningCandidateComponentProvider类
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
...
else {
return scanCandidateComponents(basePackage);
}
}
// ClassPathScanningCandidateComponentProvider类
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//拼接扫描路径,比如:classpath*:org/springframework/boot/demo/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//从 packageSearchPath 路径中扫描所有的类 资源定位
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// //判断该类是不是 @Component 注解标注的类,并且不是需要排除掉的类
if (isCandidateComponent(metadataReader)) {
//将该类封装成 ScannedGenericBeanDefinition(BeanDefinition接口的实现类)类
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
} else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
} else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
} else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}

在第13行将basePackage拼接成classpath*:org/springframework/boot/demo/**/*.class,在第16行的getResources(packageSearchPath);方法中扫描到了该路径下的所有的类。然后遍历这些Resources,在第27行判断该类是不是 @Component注解标注的类,并且不是需要排除掉的类。在第29行将扫描到的类,解析成ScannedGenericBeanDefinition,该类是BeanDefinition接口的实现类。

第三步:BeanDefinition注册
1
2
3
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

ComponentScanAnnotationParser#registerBeanDefinition完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableFactory中已经建立了整个Bean的配置信息,而这些BeanDefinition已经可以被容器使用了。他们都在BeanbefinitionMap里被检索和使用。容器的作用就是对这些信息进行处理和维护。这些信息是容器简历依赖反转的基础。

自动装配处理-DeferredImportSelector

1、预处理:deferredImportSelectorHandler.handle()
2、真正处理:deferredImportSelectorHandler.process()统一处理此次解析得到的所有DeferredImportSelector
3、ConfigurationClassPostProcessor#processConfigBeanDefinitions()中的loadBeanDefinitions()加载Bean

ConfigurationClassParser#parse()中调用了processImports()@Import注解进行解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, boolean checkForCircularImports) {
if (!importCandidates.isEmpty()) {
if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
} else {
this.importStack.push(configClass);

try {
Iterator var5 = importCandidates.iterator();

while(var5.hasNext()) {
ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var5.next();
Class candidateClass;
if (candidate.isAssignable(ImportSelector.class)) {
candidateClass = candidate.loadClass();
ImportSelector selector = (ImportSelector)BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);
//用于Spring Boot自动装配 当所有配置类处理完才执行
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
} else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames);
this.processImports(configClass, currentSourceClass, importSourceClasses, false);
}
} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
} else {
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
this.processConfigurationClass(candidate.asConfigClass(configClass));
}
}
} catch (BeanDefinitionStoreException var15) {
throw var15;
} catch (Throwable var16) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var16);
} finally {
this.importStack.pop();
}
}

}
}

在上面的方法中this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);将当前的configClass放到deferredImportSelectorHandler,留着之后处理。

预处理:deferredImportSelectorHandler.handle()

进入到handle()方法查看下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private class DeferredImportSelectorHandler {
@Nullable
private List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImportSelectors;

private DeferredImportSelectorHandler() {
this.deferredImportSelectors = new ArrayList();
}

public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
ConfigurationClassParser.DeferredImportSelectorHolder holder = new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
} else {
this.deferredImportSelectors.add(holder);
}

}
}

ConfigurationClassParser#parse()中执行完parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName())后,这时所有的类都已经处理完毕,才执行this.deferredImportSelectorHandler.process();

真正处理:deferredImportSelectorHandler.process()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private class DeferredImportSelectorHandler {
@Nullable
private List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImportSelectors;

private DeferredImportSelectorHandler() {
this.deferredImportSelectors = new ArrayList();
}

public void process() {
List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;

try {
if (deferredImports != null) {
ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();
deferredImports.sort(ConfigurationClassParser.DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
} finally {
this.deferredImportSelectors = new ArrayList();
}

}
}

DeferredImportSelectorHolder会根据Group进行分组,来区分不同的ImportSelector,比如我们这个项目的AutoConfigurationImportSelector是属于AutoConfigurationGroup类型的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
private class DeferredImportSelectorGroupingHandler {
// key:组类型(在这里 AutoConfigurationGroup) value:组
private final Map<Object, ConfigurationClassParser.DeferredImportSelectorGrouping> groupings;
// key:配置类的注解属性 value:配置类信息(在这里是 AppBootstrap 的信息)
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses;

private DeferredImportSelectorGroupingHandler() {
this.groupings = new LinkedHashMap();
this.configurationClasses = new HashMap();
}

public void register(ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) {
// 调用org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getImportGroup 返回AutoConfigurationGroup.class
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
// 创建Group对象, 用DeferredImportSelectorGrouping包装并存入到m
ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)this.groupings.computeIfAbsent(group != null ? group : deferredImport, (key) -> {
return new ConfigurationClassParser.DeferredImportSelectorGrouping(this.createGroup(group));
});
// 向deferredImports对象添加元素, 后面
grouping.add(deferredImport);
// 存入map中 key为启动类的元数据 value是启动类
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass());
}

public void processGroupImports() {
Iterator var1 = this.groupings.values().iterator();

while(var1.hasNext()) {
ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)var1.next();
grouping.getImports().forEach((entry) -> {
// 获取主配置类 启动类
ConfigurationClass configurationClass = (ConfigurationClass)this.configurationClasses.get(entry.getMetadata());

try {
// 逐个执行导入 至此执行完成后, 扫描出的类都会注册到BeanDefinition中 等待Spring容器对对象实例化和初始化
ConfigurationClassParser.this.processImports(configurationClass, ConfigurationClassParser.this.asSourceClass(configurationClass), ConfigurationClassParser.this.asSourceClasses(entry.getImportClassName()), false);
} catch (BeanDefinitionStoreException var4) {
throw var4;
} catch (Throwable var5) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", var5);
}
});
}

}

private Group createGroup(@Nullable Class<? extends Group> type) {
Class<? extends Group> effectiveType = type != null ? type : ConfigurationClassParser.DefaultDeferredImportSelectorGroup.class;
Group group = (Group)BeanUtils.instantiateClass(effectiveType);
ParserStrategyUtils.invokeAwareMethods(group, ConfigurationClassParser.this.environment, ConfigurationClassParser.this.resourceLoader, ConfigurationClassParser.this.registry);
return group;
}
}

processGroupImports()中,getImports()返回的Entry,Entry保存着要导入的类和导入此类的配置类的注解信息,如下:

1
2
3
4
5
6
class Entry {
// 引入当前类的配置类元数据,这里就是 appBootstrap 的注解元数据
private final AnnotationMetadata metadata;

// 引入类的全类名,例如:org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
private final String importClassName;

我们接着看grouping.getImports()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static class DeferredImportSelectorGrouping {
private final Group group;
private final List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = new ArrayList();

DeferredImportSelectorGrouping(Group group) {
this.group = group;
}

public void add(ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) {
this.deferredImports.add(deferredImport);
}

public Iterable<Entry> getImports() {
Iterator var1 = this.deferredImports.iterator();

while(var1.hasNext()) {
ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var1.next();
//group为AutoConfigurationGroup对象执行org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup.process方法
//deferredImport.getConfigurationClass().getMetadata()获取类路径META-INF/spring-autoconfigure-metadata.properties下的元数据信息
this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());
}

return this.group.selectImports();
}
}

这里的group对应由deferredImports.forEach(handler::register);注册进来,对应为AutoConfigurationGroup。上述方法中调用的processselectImports,其实是调用的是AutoConfigurationGroup#process()AutoConfigurationGroup#selectImports()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 private static class AutoConfigurationGroup implements Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap();
private final List<AutoConfigurationImportSelector.AutoConfigurationEntry> autoConfigurationEntries = new ArrayList();
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private AutoConfigurationMetadata autoConfigurationMetadata;

private AutoConfigurationGroup() {
}

public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}

public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}

public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}

public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
});
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(this.getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

while(var4.hasNext()) {
String importClassName = (String)var4.next();
this.entries.putIfAbsent(importClassName, annotationMetadata);
}

}

public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
} else {
Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
return new Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
}).collect(Collectors.toList());
}
}
}

AutoConfigurationGroup#process()负责加载spring.factories文件中的配置类,并按条件进行过滤,而AutoConfigurationGroup#selectImports()返回最终的配置实现类集合。

加载Bean

ConfigurationClassPostProcessor#processConfigBeanDefinitions()中的loadBeanDefinitions()完成配置类bean加载。

小结

综上可以看到DeferredImportSelector的处理过程并非是直接 调用ImportSelector#selectImports方法。而是调用DeferredImportSelector.Group#processDeferredImportSelector.Group#selectImports方法来完成引入功能。

参考资料