Spring Boot 源码剖析

Spring Boot依赖管理

问题:(1)为什么导入dependency时不需要指定版本?
Spring Boot项目的父项目依赖spring-boot-starter-parent


         
         
< parent >
< groupId >org.springframework.boot </ groupId >
< artifactId >spring-boot-starter-parent </ artifactId >
< version >2.5.1 </ version >
< relativePath /> <!-- lookup parent from repository -->
</ parent >
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

spring-boot-starter-parent中定义了jdk版本,源文件编码方式,Maven打包编译版本等。


         
         
< properties >
< java.version >1.8 </ java.version >
< resource.delimiter >@ </ resource.delimiter >
< maven.compiler.source >${java.version} </ maven.compiler.source >
< maven.compiler.target >${java.version} </ maven.compiler.target >
< project.build.sourceEncoding >UTF-8 </ project.build.sourceEncoding >
< project.reporting.outputEncoding >UTF-8 </ project.reporting.outputEncoding >
</ properties >
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

并且在build标签中定义了resource资源和pluginManagement


         
         
< resources >
< resource >
< directory >${basedir}/src/main/resources </ directory >
< filtering >true </ filtering >
< includes >
< include >**/application*.yml </ include >
< include >**/application*.yaml </ include >
< include >**/application*.properties </ include >
</ includes >
</ resource >
< resource >
< directory >${basedir}/src/main/resources </ directory >
< excludes >
< exclude >**/application*.yml </ exclude >
< exclude >**/application*.yaml </ exclude >
< exclude >**/application*.properties </ exclude >
</ excludes >
</ resource >
</ resources >
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

里面定义了资源过滤,针对 application 的 yml 、 properties 格式进行了过滤,可以支持不同环境的配置,比如 application-dev.yml 、 application-test.yml 、 application-dev.properties 、 application-dev.properties 等等。

spring-boot-starter-parent项目的父项目是spring-boot-dependencies


         
         
< parent >
< groupId >org.springframework.boot </ groupId >
< artifactId >spring-boot-dependencies </ artifactId >
< version >2.5.1 </ version >
</ parent >
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

在这个项目中定义了大量的依赖项目的版本


         
         
< properties >
< activemq.version >5.16.2 </ activemq.version >
< antlr2.version >2.7.7 </ antlr2.version >
< appengine-sdk.version >1.9.89 </ appengine-sdk.version >
< artemis.version >2.17.0 </ artemis.version >
< aspectj.version >1.9.6 </ aspectj.version >
< assertj.version >3.19.0 </ assertj.version >
< atomikos.version >4.0.6 </ atomikos.version >
< awaitility.version >4.0.3 </ awaitility.version >
< build-helper-maven-plugin.version >3.2.0 </ build-helper-maven-plugin.version >
< byte-buddy.version >1.10.22 </ byte-buddy.version >
......
</ properties >
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

spring-boot-dependencies的dependencyManagement节点
在这里,dependencies定义了SpringBoot版本的依赖的组件以及相应版本。


         
         
< 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 >
.......
</ dependencyManagement >
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

因为存在这个依赖关系所以在我们创建的Spring Boot项目中部分依赖不需要写版本号。

(2)问题2: spring-boot-starter-parent父依赖启动器的主要作用是进行版本统一管理,那么项目
运行依赖的JAR包是从何而来的?

spring-boot-starter-web
查看spring-boot-starter-web依赖文件源码,核心代码具体如下


         
         
< dependency >
< groupId >org.springframework.boot </ groupId >
< artifactId >spring-boot-starter </ artifactId >
< version >2.5.1 </ version >
< scope >compile </ scope >
</ dependency >
< dependency >
< groupId >org.springframework.boot </ groupId >
< artifactId >spring-boot-starter-json </ artifactId >
< version >2.5.1 </ version >
< scope >compile </ scope >
</ dependency >
< dependency >
< groupId >org.springframework.boot </ groupId >
< artifactId >spring-boot-starter-tomcat </ artifactId >
< version >2.5.1 </ version >
< scope >compile </ scope >
</ dependency >
< dependency >
< groupId >org.springframework </ groupId >
< artifactId >spring-web </ artifactId >
< version >5.3.8 </ version >
< scope >compile </ scope >
</ dependency >
< dependency >
< groupId >org.springframework </ groupId >
< artifactId >spring-webmvc </ artifactId >
< version >5.3.8 </ version >
< scope >compile </ scope >
</ dependency >
  • 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.

可见在spring-boot-starter-web依赖启动器中打包了web开发所需要的底层所有依赖。

因此在引入spring-boot-starter-web依赖启动器时,就可以实现web场景开发,而不需要额外导入tomcat服务器及其他web依赖文件。

Spring Boot除了提供Web依赖启动器之外,还提供了其他很多依赖启动器。具体可以到官网查找。


自动配置

自动配置:根据我们添加的jar包依赖,Spring Boot会将一些配置类的bean注册进ioc容器,我们可以在需要的地方直接使用。

Spring Boot是如何进行自动配置的,都把哪些组件进行了自动配置?

Spring Boot的启动入口是一个被@SpringBootApplication注解的类的main方法。


@SpringBootApplication

查看@SpringBootApplication源码:


         
         
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org. springframework. boot. autoconfigure;

import java. lang. annotation. Documented;
import java. lang. annotation. ElementType;
import java. lang. annotation. Inherited;
import java. lang. annotation. Retention;
import java. lang. annotation. RetentionPolicy;
import java. lang. annotation. Target;
import org. springframework. beans. factory. support. BeanNameGenerator;
import org. springframework. boot. SpringBootConfiguration;
import org. springframework. boot. context. TypeExcludeFilter;
import org. springframework. context. annotation. ComponentScan;
import org. springframework. context. annotation. Configuration;
import org. springframework. context. annotation. FilterType;
import org. springframework. context. annotation. ComponentScan. Filter;
import org. springframework. core. annotation. AliasFor;

@Target({ ElementType. TYPE})
@Retention( RetentionPolicy. RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 标明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan(
excludeFilters = { @Filter(
type = FilterType. CUSTOM,
classes = { TypeExcludeFilter. class}
), @Filter(
type = FilterType. CUSTOM,
classes = { AutoConfigurationExcludeFilter. class}
)}
)
public @interface SpringBootApplication {
// 根据class来排除特定的类,使其不能加入spring容器,传入参数value类型是class类型。
@AliasFor(
annotation = EnableAutoConfiguration. class
)
Class <?>[] exclude() default {};

// 根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全 类名字符串数组。
@AliasFor(
annotation = EnableAutoConfiguration. class
)
String[] excludeName() default {};

// 指定扫描包,参数是包名的字符串数组。
@AliasFor(
annotation = ComponentScan. class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};

// 扫描特定的包,参数类似是Class类型数组。
@AliasFor(
annotation = ComponentScan. class,
attribute = "basePackageClasses"
)
Class <?>[] scanBasePackageClasses() default {};

@AliasFor(
annotation = ComponentScan. class,
attribute = "nameGenerator"
)
Class <? extends BeanNameGenerator > nameGenerator() default BeanNameGenerator. class;

@AliasFor(
annotation = Configuration. class
)
boolean proxyBeanMethods() default true;
}
  • 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.

从源码可以看出,@SpringBootApplication是一个组合注解,前面4个是注解的元数据信息,后面三个注解是核心:@SpringBootConfiguration、@EnableAutoConfiguration、
@ComponentScan

@SpringBootConfiguration

查看源码:


         
         
@Target({ ElementType. TYPE})
@Retention( RetentionPolicy. RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration. class
)
boolean proxyBeanMethods() default true;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

从源码看到,它是一个被@Configuration标记的注解,没有其他的代码。说明它其实就是一个@Configuration的包装,重新命名,功能相同。

被@SpringBootConfiguration 标记的类就是一个配置类。

@EnableAutoConfiguration

查看源码:


         
         
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org. springframework. boot. autoconfigure;

import java. lang. annotation. Documented;
import java. lang. annotation. ElementType;
import java. lang. annotation. Inherited;
import java. lang. annotation. Retention;
import java. lang. annotation. RetentionPolicy;
import java. lang. annotation. Target;
import org. springframework. context. annotation. Import;

@Target({ ElementType. TYPE})
@Retention( RetentionPolicy. RUNTIME)
@Documented
@Inherited
// 自动配置包
@AutoConfigurationPackage
// Spring的底层注解@Import,给容器中导入一个组件;
// 导入的组件是AutoConfigurationPackages.Registrar.class
@Import({ AutoConfigurationImportSelector. class})
// 告诉SpringBoot开启自动配置功能,这样自动配置才能生效。
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 返回不会被导入到 Spring 容器中的类
Class <?>[] exclude() default {};
// 返回不会被导入到 Spring 容器中的类名
String[] excludeName() default {};
}
  • 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.

@EnableAutoConfiguration是一个组合注解:是@AutoConfigurationPackage 和@Import的组合。


【Spring Boot源码剖析之Spring Boot源码剖析】_spring



Spring中很多以Enable开头的注解,其作用就是借助@Import来收集并注册特定场景相关的Bean,并加载到IOC容器。

@EnableAutoConfiguration就是借助@Import来收集所有符合自动配置条件的bean定义,并加载到IoC容器。
它引入了AutoConfigurationImportSelector类。

下面我们继续深入到@AutoConfigurationPackage 和@Import这两个注解中去看看它们做了什么工作。

@AutoConfigurationPackage

查看源码:


         
         
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org. springframework. boot. autoconfigure;

import java. lang. annotation. Documented;
import java. lang. annotation. ElementType;
import java. lang. annotation. Inherited;
import java. lang. annotation. Retention;
import java. lang. annotation. RetentionPolicy;
import java. lang. annotation. Target;
import org. springframework. boot. autoconfigure. AutoConfigurationPackages. Registrar;
import org. springframework. context. annotation. Import;

@Target({ ElementType. TYPE})
@Retention( RetentionPolicy. RUNTIME)
@Documented
@Inherited
@Import({ Registrar. class}) // 导入Registrar中注册的组件
public @interface AutoConfigurationPackage {
String[] basePackages() default {};

Class <?>[] basePackageClasses() default {};
}
  • 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.

@AutoConfigurationPackage 注解引入Registrar类


         
         
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}

/**
* 注册BeanDefinition
*/
public void registerBeanDefinitions( AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 根据注解元信息,拿到要扫描的包,扫描并注册到BeanDefinitionRegistry中
AutoConfigurationPackages. register( registry, ( String[])( new AutoConfigurationPackages. PackageImports( metadata)). getPackageNames(). toArray( new String[ 0]));
}

public Set < Object > determineImports( AnnotationMetadata metadata) {
return Collections. singleton( new AutoConfigurationPackages. PackageImports( metadata));
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

register方法:


         
         
public static void register( BeanDefinitionRegistry registry, String... packageNames) {
// 第一次进到这个方法的时候private static final String BEAN = AutoConfigurationPackages.class.getName(); 并没有在register中注册。
if ( registry. containsBeanDefinition( BEAN)) {
AutoConfigurationPackages. BasePackagesBeanDefinition beanDefinition = ( AutoConfigurationPackages. BasePackagesBeanDefinition) registry. getBeanDefinition( BEAN);
beanDefinition. addBasePackages( packageNames);
} else {
// 注册BeanDefinition
registry. registerBeanDefinition( BEAN, new AutoConfigurationPackages. BasePackagesBeanDefinition( packageNames));
}

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

在register中注册的是AutoConfigurationPackages的内部类BasePackagesBeanDefinition。
BasePackagesBeanDefinition源码:


         
         
static final class BasePackagesBeanDefinition extends GenericBeanDefinition {
private final Set < String > basePackages = new LinkedHashSet();

BasePackagesBeanDefinition( String... basePackages) {
// Class对象是BasePackages
this. setBeanClass( AutoConfigurationPackages. BasePackages. class);
this. setRole( 2);
// 保存基础包
this. addBasePackages( basePackages);
}

public Supplier <?> getInstanceSupplier() {
return () -> {
return new AutoConfigurationPackages. BasePackages( StringUtils. toStringArray( this. basePackages));
};
}

private void addBasePackages( String[] additionalBasePackages) {
this. basePackages. addAll( Arrays. asList( additionalBasePackages));
}
}

static final class BasePackages {
private final List < String > packages;
private boolean loggedBasePackageInfo;

BasePackages( String... names) {
List < String > packages = new ArrayList();
String[] var3 = names;
int var4 = names. length;

for( int var5 = 0; var5 < var4; ++ var5) {
String name = var3[ var5];
if ( StringUtils. hasText( name)) {
packages. add( name);
}
}

this. packages = packages;
}

List < String > get() {
if ( ! this. loggedBasePackageInfo) {
if ( this. packages. isEmpty()) {
if ( AutoConfigurationPackages. logger. isWarnEnabled()) {
AutoConfigurationPackages. logger. warn( "@EnableAutoConfiguration was declared on a class in the default package. Automatic @Repository and @Entity scanning is not enabled.");
}
} else if ( AutoConfigurationPackages. logger. isDebugEnabled()) {
String packageNames = StringUtils. collectionToCommaDelimitedString( this. packages);
AutoConfigurationPackages. logger. debug( "@EnableAutoConfiguration was declared on a class in the package '" + packageNames + "'. Automatic @Repository and @Entity scanning is enabled.");
}

this. loggedBasePackageInfo = true;
}

return this. packages;
}
}
  • 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.

BasePackagesBeanDefinition 继承了GenericBeanDefinition,说明它就是一个Spring的BeanDefinition。只是在这个BeanDefinition中保存的Class并不是AutoConfigurationPackages,而是
BasePackages。

因此@EnableAutoConfiguration中的@AutoConfigurationPackage使用@Import({Registrar.class})注解,向Ioc容器中注册了一个BasePackages的Bean定义。

用图来描述一下整个过程:


【Spring Boot源码剖析之Spring Boot源码剖析】_maven_02


@Import({AutoConfigurationImportSelector.class})

@Import({AutoConfigurationImportSelector.class}) :将
AutoConfigurationImportSelector 这个类导入到 Spring 容器中,
AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IOC 容器( ApplicationContext )中。


【Spring Boot源码剖析之Spring Boot源码剖析】_java_03


可以看到 AutoConfigurationImportSelector 重点是实现了 DeferredImportSelector 接口和各种 Aware 接口,然后 DeferredImportSelector 接口又继承了 ImportSelector 接口。
其不光实现了 ImportSelector 接口,还实现了很多其它的 Aware 接口,分别表示在某个时机会被回调。


自动配置实现逻辑的入口方法:

AutoConfigurationImportSelector将会被放到DeferredImportSelectorGrouping中的deferredImports集合中。并且在DeferredImportSelectorGrouping的getImports()方法中进行统一处理。

跟自动配置逻辑相关的入口方法在 DeferredImportSelectorGrouping 类的 getImports 方法处.


         
         
public Iterable < Entry > getImports() {
Iterator var1 = this. deferredImports. iterator();
// 遍历DeferredImportSelectorGrouping的deferredImports集合。AutoConfigurationImportSelector也在这个集合中。
while( var1. hasNext()) {
ConfigurationClassParser. DeferredImportSelectorHolder deferredImport = ( ConfigurationClassParser. DeferredImportSelectorHolder) var1. next();

// (1)利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类
this. group. process( deferredImport. getConfigurationClass(). getMetadata(), deferredImport. getImportSelector());
}
// (2)经过上面的处理后,然后再进行选择导入哪些配置类
return this. group. selectImports();
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

其中重要的两个步骤:

  • 利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类
  • 经过上面的处理后,然后再进行选择导入哪些配置类
分析自动配置的主要逻辑

AutoConfigurationImportSelector的process方法:


         
         
// 这里用来处理自动配置类,比如过滤掉不符合匹配条件的自动配置类
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());
});
// (1)调用getAutoConfigurationEntry方法得到自动配置类放入 autoConfigurationEntry对象中
AutoConfigurationImportSelector. AutoConfigurationEntry autoConfigurationEntry = (( AutoConfigurationImportSelector) deferredImportSelector). getAutoConfigurationEntry( annotationMetadata);
// (2)又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合
this. autoConfigurationEntries. add( autoConfigurationEntry);
Iterator var4 = autoConfigurationEntry. getConfigurations(). iterator();
// (3)遍历刚获取的自动配置类,放入entries
while( var4. hasNext()) {
String importClassName = ( String) var4. next();
// 这里符合条件的自动配置类作为key,annotationMetadata作为值放进entries集合
this. entries. putIfAbsent( importClassName, annotationMetadata);
}

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

其中重要的三个步骤:

  • 调用getAutoConfigurationEntry方法得到自动配置类放入 autoConfigurationEntry对象中
  • 又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合
  • 遍历刚获取的自动配置类,放入entries

获取自动配置类方法getAutoConfigurationEntry:


         
         
protected AutoConfigurationImportSelector. AutoConfigurationEntry getAutoConfigurationEntry( AnnotationMetadata annotationMetadata) {
if ( ! this. isEnabled( annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this. getAttributes( annotationMetadata);
// (1)得到spring.factories文件配置的所有自动配置类
List < String > configurations = this. getCandidateConfigurations( annotationMetadata, attributes);
// 利用LinkedHashSet移除重复的配置类
configurations = this. removeDuplicates( configurations);
// 得到要排除的自动配置类,比如注解属性exclude的配置类
// 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
// 将会获取到exclude = FreeMarkerAutoConfiguration.class的注解数据
Set < String > exclusions = this. getExclusions( annotationMetadata, attributes);
// 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常
this. checkExcludedClasses( configurations, exclusions);
// (2)将要排除的配置类移除
configurations. removeAll( exclusions);
// (3)对加载到的自动配置类按需要进行过滤
configurations = this. getConfigurationClassFilter(). filter( configurations);
// (4)获取了符合条件的自动配置类后,此时触发AutoConfigurationImportEvent事件
// 目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
// 该事件什么时候会被触发?--> 在刷新容器时调用invokeBeanFactoryPostProcessors后置处理器时触发
this. fireAutoConfigurationImportEvents( configurations, exclusions);
// (5)将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回
return new AutoConfigurationImportSelector. AutoConfigurationEntry( configurations, exclusions);
}
}
  • 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.

其中重要的五个步骤:

  • 得到spring.factories文件配置的所有自动配置类
  • 将要排除的配置类移除
  • 对加载到的自动配置类按需要进行过滤
  • 告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
  • 将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回

getCandidateConfigurations获取配置文件中的自动配置类过程分析
这个方法中重要的方法是loadFactoryNames,这个方法的作用是加载一些组件的名字。


         
         
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;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

进入loadFactoryNames方法,它只是做了简单验证,就委托另外一个方法loadSpringFactories 去加载组件名。


         
         
public static List < String > loadFactoryNames( Class <?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if ( classLoader == null) {
classLoaderToUse = SpringFactoriesLoader. class. getClassLoader();
}

String factoryTypeName = factoryType. getName();
// 真正去加载组件名的方法
return ( List) loadSpringFactories( classLoaderToUse). getOrDefault( factoryTypeName, Collections. emptyList());
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

继续进入loadSpringFactories方法:


         
         
private static Map < String, List < String >> loadSpringFactories( ClassLoader classLoader) {
Map < String, List < String >> result = ( Map) cache. get( classLoader);
if ( result != null) {
return result;
} else {
HashMap result = new HashMap();

try {

// 加载类路径下spring.factories文件,将其中设置的配置类的全路径信息封装 为Enumeration类对象
Enumeration urls = classLoader. getResources( "META-INF/spring.factories");

//循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入 的键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中
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 factoryTypeName = (( String) entry. getKey()). trim();
String[] factoryImplementationNames = StringUtils. commaDelimitedListToStringArray(( String) entry. getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames. length;

for( int var12 = 0; var12 < var11; ++ var12) {
String factoryImplementationName = var10[ var12];
(( List) result. computeIfAbsent( factoryTypeName, ( key) -> {
return new ArrayList();
})). add( factoryImplementationName. trim());
}
}
}

result. replaceAll(( factoryType, implementations) -> {
return ( List) implementations. stream(). distinct(). collect( Collectors. collectingAndThen( Collectors. toList(), Collections:: unmodifiableList));
});
cache. put( classLoader, result);
return result;
} catch ( IOException var14) {
throw new IllegalArgumentException( "Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
  • 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.

这个方法的主要作用是遍历整个ClassLoader下所有的jar包下的Spring.factories文件。


【Spring Boot源码剖析之Spring Boot源码剖析】_java_04



而在spring-boot-autoconfigure包的META-INF下的spring.factories中保存着springboot的默认提供的自动配置类。

【Spring Boot源码剖析之Spring Boot源码剖析】_java_05


我们下面总结下 getAutoConfigurationEntry 方法主要做的事情:
【1】从 spring.factories 配置文件中加载 EnableAutoConfiguration 自动配置类),获取的自动配
置类如图所示。
【2】若 @EnableAutoConfiguration 等注解标有要 exclude 的自动配置类,那么再将这个自动配置类排除掉;
【3】排除掉要 exclude 的自动配置类后,然后再调用 filter 方法进行进一步的过滤,再次排除一些
不符合条件的自动配置类;
【4】经过重重过滤后,此时再触发 AutoConfigurationImportEvent 事件,告诉
ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类;
【5】 最后再将符合条件的自动配置类返回。

总结了 getAutoConfigurationEntry 方法主要的逻辑后,我们再来细看一下
AutoConfigurationImportSelector 的 filter 方法:


         
         
List < String > filter( List < String > configurations) {
long startTime = System. nanoTime();

// 将从spring.factories中获取的自动配置类转出字符串数组
String[] candidates = StringUtils. toStringArray( configurations);
boolean skipped = false;
Iterator var6 = this. filters. iterator();

int i;
while( var6. hasNext()) {
AutoConfigurationImportFilter filter = ( AutoConfigurationImportFilter) var6. next();

// 判断各种filter来判断每个candidate(这里实质要通过candidate(自动配置类)拿到其标注的
// @ConditionalOnClass,@ConditionalOnBean和@ConditionalOnWebApplication里面的注解值)是否匹配,
boolean[] match = filter. match( candidates, this. autoConfigurationMetadata);

// 遍历match数组,注意match顺序跟candidates的自动配置类一一对应
for( i = 0; i < match. length; ++ i) {
// 若有不匹配的话
if ( ! match[ i]) {
candidates[ i] = null;
// 标注skipped为true
skipped = true;
}
}
}

// 这里表示若所有自动配置类经过OnBeanCondition,OnClassCondition和 OnWebApplicationCondition过滤后,全部都匹配的话,则全部原样返回
if ( ! skipped) {
return configurations;
} else {
// 建立result集合来装匹配的自动配置类
List < String > result = new ArrayList( candidates. length);
String[] var12 = candidates;
int var14 = candidates. length;

for( i = 0; i < var14; ++ i) {
String candidate = var12[ i];
// 符合条件的自动配置类,此时添加到result集合中
if ( candidate != null) {
result. add( candidate);
}
}

if ( AutoConfigurationImportSelector. logger. isTraceEnabled()) {
int numberFiltered = configurations. size() - result. size();
AutoConfigurationImportSelector. logger. trace( "Filtered " + numberFiltered + " auto configuration class in " + TimeUnit. NANOSECONDS. toMillis( System. nanoTime() - startTime) + " ms");
}
// 最后返回符合条件的自动配置类
return result;
}
}
  • 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.

AutoConfigurationImportSelector 的 filter 方法主要做的事情就是调用AutoConfigurationImportFilter 接口的match 方法来判断每一个自动配置类上的条件注解(若有的话) @ConditionalOnClass ,@ConditionalOnBean 或 @ConditionalOnWebApplication 是否满足条件,若满足,则返回true,说明匹配,若不满足,则返回false说明不匹配。

我们现在知道 AutoConfigurationImportSelector 的 filter 方法主要做了什么事情就行了,现在先不用研究的过深

有选择的导入自动配置类
this.group.selectImports 方法是如何进一步有选择的导入自动配置类的。


         
         
public Iterable < Entry > selectImports() {
if ( this. autoConfigurationEntries. isEmpty()) {
return Collections. emptyList();
} else {
// 这里得到所有要排除的自动配置类的set集合
Set < String > allExclusions = ( Set) this. autoConfigurationEntries. stream(). map( AutoConfigurationImportSelector. AutoConfigurationEntry:: getExclusions). flatMap( Collection:: stream). collect( Collectors. toSet());
// 这里得到经过滤后所有符合条件的自动配置类的set集合
Set < String > processedConfigurations = ( Set) this. autoConfigurationEntries. stream(). map( AutoConfigurationImportSelector. AutoConfigurationEntry:: getConfigurations). flatMap( Collection:: stream). collect( Collectors. toCollection( LinkedHashSet:: new));

// 移除掉要排除的自动配置类
processedConfigurations. removeAll( allExclusions);
// 对标注有@Order注解的自动配置类进行排序
return ( Iterable) this. sortAutoConfigurations( processedConfigurations, this. getAutoConfigurationMetadata()). stream(). map(( importClassName) -> {
return new Entry(( AnnotationMetadata) this. entries. get( importClassName), importClassName);
}). collect( Collectors. toList());
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

selectImports 方法的自动配置类再进一步排除 exclude 的自动配置类,然后再排序
最后,我们再总结下SpringBoot自动配置的原理,主要做了以下事情:

  1. 从spring.factories配置文件中加载自动配置类;
  2. 加载的自动配置类中排除掉 @EnableAutoConfiguration 注解的 exclude 属性指定的自动配置类;
  3. 然后再用 AutoConfigurationImportFilter 接口去过滤自动配置类是否符合其标注注解(若有标注的话) @ConditionalOnClass , @ConditionalOnBean 和 @ConditionalOnWebApplication 的条件,若都符合的话则返回匹配结果;
  4. 然后触发 AutoConfigurationImportEvent 事件,告诉 ConditionEvaluationReport 条件评估报告器对象来分别记录符合条件和 exclude 的自动配置类。
  5. 最后spring再将最后筛选后的自动配置类导入IOC容器中

画图梳理一下这个流程


【Spring Boot源码剖析之Spring Boot源码剖析】_java_06


总结:

  • 在当前Spring Boot工程中,各个组件(jar包),都会存在一个名称为META-INF的目录,目录中有一个sping.factories文件。
  • 在这个文件中会配置工厂类的全路径如MyBatis中配置了org.mybatis.spring.boot.autoconfigure.MyBatisAutoConfiguration。
  • Spring Boot 通过@EnableAutoConfiguration注解收集配置工厂类
  • 由Spring创建bean实例存到IoC容器中。

【Spring Boot源码剖析之Spring Boot源码剖析】_spring_07


条件注解

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

  • @ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
  • @ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
  • @ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。基于SpEL表达式 的条件判断。
  • @ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
  • @ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。
  • @ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。
  • @ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
  • @ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
  • @ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
  • @ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
  • @ConditionalOnResource:当类路径下有指定的资源时触发实例化。
  • @ConditionalOnJndi:在JNDI存在的条件下触发实例化。
  • @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首 选的Bean时触发实例化。

@ComponentScan

  • @ConponentScan用于配置扫描Spring的扫包基础路径的注解。默认扫描被这个注解标记的类的目录及子目录。
  • @SpringBootApplication中包含了@ConponentScan,因此它具备@ConponentScan相同的功能,因此被@SpringBootApplication注解标记了的类的类路径及其子路径上的bean都能被扫描到。这也是为什么非这个目录及其子目录中的bean不能被扫描的原因。

SpringApplication初始化过程

@SpringBootApplication中的@ComponentScan是在什么时候被扫描到的?
SpringBoot项目的main方法:


         
         
@SpringBootApplication
@EnableConfigurationProperties
public class SpringBootDemoApplication {

public static void main( String[] args) {
SpringApplication. run( SpringBootDemoApplication. class, args);
}

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

从run方法开始追踪:


         
         
public static ConfigurableApplicationContext run( Class <?> primarySource, String... args) {
return run( new Class[]{ primarySource}, args);
}
  • 1.
  • 2.
  • 3.

调用了另外一个run方法:


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

这个run方法构建了一个SpringApplication类的实例,我们继续来看下SpringApplication类的构造函数

SpringApplication构造函数


         
         
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 = Collections. emptySet();
this. isCustomEnvironment = false;
this. lazyInitialization = false;
this. applicationContextFactory = ApplicationContextFactory. DEFAULT;
this. applicationStartup = ApplicationStartup. DEFAULT;
this. resourceLoader = resourceLoader;
Assert. notNull( primarySources, "PrimarySources must not be null");
this. primarySources = new LinkedHashSet( Arrays. asList( primarySources));
// 1、推断应用类型:servlet、reactive、none
this. webApplicationType = WebApplicationType. deduceFromClasspath();
this. bootstrapRegistryInitializers = this. getBootstrapRegistryInitializersFromSpringFactories();
// 2、初始化META-INF/spring.factories文件中配置的ApplicationContextInitializer
this. setInitializers( this. getSpringFactoriesInstances( ApplicationContextInitializer. class));
// 3、初始化classpath下的所有已配置的ApplicationListener
this. setListeners( this. getSpringFactoriesInstances( ApplicationListener. class));
// 4、推断出main方法的类名。
this. mainApplicationClass = this. deduceMainApplicationClass();
}
  • 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.
判断应用类型


         
         
private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{ "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};

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;
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

判断应用类型主要分三个步骤:

  • 1、判断类路径中有没有DispatcherHandler,DispatcherServlet,ServletContainer:如果有DispatcherHandler,没有DispatcherServlet,ServletContainer则是REACTIVE(响应式编程)类型的应用。
  • 2、如果不满足条件1,如果类路径不包含Servlet类或不包含ConfigurableWebApplicationContext类,则是NONE(不是一个web应用)类型应用。
  • 如果条件1、2都不满足,那它就是一个Servlet(web应用,需要启动内置servlet容器)应用。
初始化ApplicationContextInitializer


         
         
this. setInitializers( this. getSpringFactoriesInstances( ApplicationContextInitializer. class));
  • 1.


         
         
private < T > Collection < T > getSpringFactoriesInstances( Class < T > type, Class <?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this. getClassLoader();
// 从MATA-INF/spring.factories文件中读取所有ApplicationContextInitializer类全路径为key的所有类全路径
Set < String > names = new LinkedHashSet( SpringFactoriesLoader. loadFactoryNames( type, classLoader));
// 反射实例化这些ApplicationContextInitializer类
List < T > instances = this. createSpringFactoriesInstances( type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator. sort( instances);
return instances;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

主要完成了两步操作:

  • 从MATA-INF/spring.factories文件中读取所有ApplicationContextInitializer类全路径为key的所有类全路径。
  • 反射实例化上一步获取到的类,并放入到一个集合中。
初始化classpath下的所有ApplicationListener


         
         
this. setListeners( this. getSpringFactoriesInstances( ApplicationListener. class));
` `
加载过程跟初始化器一样
主要完成了两步操作:
- 从MATA - INF / spring. factories文件中读取所有ApplicationListener类全路径为key的所有类全路径。
- 反射实例化上一步获取到的类,并放入到一个集合中。

# # # # 根据调用栈,推断出main方法的类
` ` `java
this. mainApplicationClass = this. deduceMainApplicationClass();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.


         
         
private Class <?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = ( new RuntimeException()). getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace. length;

for( int var4 = 0; var4 < var3; ++ var4) {
StackTraceElement stackTraceElement = var2[ var4];
if ( "main". equals( stackTraceElement. getMethodName())) {
return Class. forName( stackTraceElement. getClassName());
}
}
} catch ( ClassNotFoundException var6) {
}

return null;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

这一步的主要功能是:

  • 根据调用栈,找到main方法所在类的class对象,存储到成员变量。


SpringApplication.run方法流程介绍

SpringApplication对象构建完后调用run方法,run方法代码如下:


         
         
public ConfigurableApplicationContext run( String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch. start();
DefaultBootstrapContext bootstrapContext = this. createBootstrapContext();
ConfigurableApplicationContext context = null;
this. configureHeadlessProperty();
//从META-INF/spring.factories中获取监听器
//1、获取并启动监听器
SpringApplicationRunListeners listeners = this. getRunListeners( args);
listeners. starting( bootstrapContext, this. mainApplicationClass);

try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments( args);
//2、构造应用上下文环境
ConfigurableEnvironment environment = this. prepareEnvironment( listeners, bootstrapContext, applicationArguments);
this. configureIgnoreBeanInfo( environment);
Banner printedBanner = this. printBanner( environment);
//3、初始化应用上下文
context = this. createApplicationContext();
context. setApplicationStartup( this. applicationStartup);
//4、刷新应用上下文前的准备阶段
this. prepareContext( bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//5、刷新应用上下文
this. refreshContext( context);
//6、刷新应用上下文后的扩展接口
this. afterRefresh( context, applicationArguments);
stopWatch. stop();
if ( this. logStartupInfo) {
( new StartupInfoLogger( this. mainApplicationClass)). logStarted( this. getApplicationLog(), stopWatch);
}

listeners. started( context);
this. callRunners( context, applicationArguments);
} catch ( Throwable var10) {
this. handleRunFailure( context, var10, listeners);
throw new IllegalStateException( var10);
}

try {
listeners. running( context);
return context;
} catch ( Throwable var9) {
this. handleRunFailure( context, var9, ( SpringApplicationRunListeners) null);
throw new IllegalStateException( var9);
}
}
  • 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.

在以上的代码中,启动过程中的重要步骤共分为六步 :

  • 1、获取并启动监听器
  • 2、构造应用上下文环境
  • 3、初始化应用上下文
  • 4、刷新应用上下文前的准备阶段
  • 5、刷新应用上下文
  • 6、刷新应用上下文后的扩展接口

获取并启动监听器

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

获取监听器的入口代码:


         
         
SpringApplicationRunListeners listeners = this. getRunListeners( args);
listeners. starting( bootstrapContext, this. mainApplicationClass);
  • 1.
  • 2.

进入到方法中:


         
         
private SpringApplicationRunListeners getRunListeners( String[] args) {
Class <?>[] types = new Class <?>[] { SpringApplication. class, String[]. class };
return new SpringApplicationRunListeners( logger,
// 从META-INF/spring.factories文件中获取监听器
getSpringFactoriesInstances( SpringApplicationRunListener. class, types, this, args),
this. applicationStartup);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

从代码中看到,在构建SpringApplicationRunListeners实例之前调用了一个我们非常熟悉的方法:getSpringFactoriesInstances,该方法的主要作用是从spring.factories文件中加载SpringApplicationRunListener类全路径为key的所有类,并实例化。


         
         
private < T > Collection < T > getSpringFactoriesInstances( Class < T > type, Class <?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 根据传入的type类型,使用type的类名全路径作为key,到META-INF/spring.factories文件中去查找值,并放到names中
Set < String > names = new LinkedHashSet <>( SpringFactoriesLoader. loadFactoryNames( type, classLoader));
// 将查找到的类通过反射实例化
List < T > instances = createSpringFactoriesInstances( type, parameterTypes, classLoader, args, names);
// 按order注解顺序排序
AnnotationAwareOrderComparator. sort( instances);
return instances;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

获取到监听器后,启动监听器:


         
         
listeners. starting( bootstrapContext, this. mainApplicationClass);
  • 1.

构造应用上下文环境

用上下文环境包括什么呢?包括计算机的环境,Java环境,Spring的运行环境,Spring项目的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等等。


         
         
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
//创建并配置相应的环境
ConfigurableEnvironment environment = this. getOrCreateEnvironment();
//根据用户配置,配置 environment系统环境
this. configureEnvironment(( ConfigurableEnvironment) environment, applicationArguments. getSourceArgs());
// 启动相应的监听器,其中一个重要的监听器 ConfigFileApplicationListener 就是加载项目配置文件的监听器。
ConfigurationPropertySources. attach(( Environment) environment);
listeners. environmentPrepared( bootstrapContext, ( ConfigurableEnvironment) environment);
DefaultPropertiesPropertySource. moveToEnd(( ConfigurableEnvironment) environment);
Assert. state( !(( ConfigurableEnvironment) environment). containsProperty( "spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
this. bindToSpringApplication(( ConfigurableEnvironment) environment);
if ( ! this. isCustomEnvironment) {
environment = ( new EnvironmentConverter( this. getClassLoader())). convertEnvironmentIfNecessary(( ConfigurableEnvironment) environment, this. deduceEnvironmentClass());
}

ConfigurationPropertySources. attach(( Environment) environment);
return ( ConfigurableEnvironment) environment;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

getOrCreateEnvironment方法做了什么?


         
         
private ConfigurableEnvironment getOrCreateEnvironment() {
if ( this. environment != null) {
return this. environment;
}
switch ( this. webApplicationType) {
case SERVLET:
// 如果应用类型是SERVLET,则创建Servlet类型的环境
return new ApplicationServletEnvironment();
case REACTIVE:
// 如果应用类型是响应式编程的,则创建Reactive类型的环境
return new ApplicationReactiveWebEnvironment();
default:
return new ApplicationEnvironment();
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

ApplicationServletEnvironment类型的继承关系:


【Spring Boot源码剖析之Spring Boot源码剖析】_maven_08



         
         
/**
* Template method delegating to
* {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
* {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
* Override this method for complete control over Environment customization, or one of
* the above for fine-grained control over property sources or profiles, respectively.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureProfiles(ConfigurableEnvironment, String[])
* @see #configurePropertySources(ConfigurableEnvironment, String[])
*/
protected void configureEnvironment( ConfigurableEnvironment environment, String[] args) {
if ( this. addConversionService) {
environment. setConversionService( new ApplicationConversionService());
}
// 将main方法的参数分组到SimpleCommandLinePropertySource中
configurePropertySources( environment, args);
// 激活相关的配置文件
configureProfiles( environment, args);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

初始化应用上下文


         
         
/**
*用于创建 {@link ApplicationContext} 的策略方法
* Strategy method used to create the {@link ApplicationContext}. By default this
* method will respect any explicitly set application context class or factory before
* falling back to a suitable default.
* @return the application context (not yet refreshed)
* @see #setApplicationContextClass(Class)
* @see #setApplicationContextFactory(ApplicationContextFactory)
*/
protected ConfigurableApplicationContext createApplicationContext() {
// 根据应用类型创建对应的 ApplicationContext实例
return this. applicationContextFactory. create( this. webApplicationType);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

调试进入:

org.springframework.boot.ApplicationContextFactory


         
         
ApplicationContextFactory DEFAULT = ( webApplicationType) -> {
try {
switch ( webApplicationType) {
case SERVLET:
// 应用类型是Servlet时,new一个AnnotationConfigServletWebServerApplicationContext上下文实例对象
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch ( Exception ex) {
throw new IllegalStateException( "Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

AnnotationConfigServletWebServerApplicationContext的类继承关系:


【Spring Boot源码剖析之Spring Boot源码剖析】_spring_09


GenericApplicationContext是AnnotationConfigServletWebServerApplicationContext的父类。

创建对象的时候,创建了一个reader和一个scanner:


         
         
/**
* Create a new {@link AnnotationConfigServletWebServerApplicationContext} that needs
* to be populated through {@link #register} calls and then manually
* {@linkplain #refresh refreshed}.
*/
public AnnotationConfigServletWebServerApplicationContext() {
this. reader = new AnnotatedBeanDefinitionReader( this);
this. scanner = new ClassPathBeanDefinitionScanner( this);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

另外在调用AnnotationConfigServletWebServerApplicationContext构造函数之前,要先调用父类的构造函数:


         
         
public GenericApplicationContext() {
this. customClassLoader = false;
this. refreshed = new AtomicBoolean();
this. beanFactory = new DefaultListableBeanFactory();
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

AnnotationConfigServletWebServerApplicationContext中的IoC容器在其父类的构造方法中被实例化。

【Spring Boot源码剖析之Spring Boot源码剖析】_java_10


刷新应用上下文前的准备阶段

刷新应用上下文前的准备阶段,就是想上下文context中设置一些属性,完成bean对象的创建。


         
         
private void prepareContext( DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置容器环境
context. setEnvironment( environment);
// 执行容器后置处理器,向context中设置值
postProcessApplicationContext( context);
// 遍历初始化器,执行初始化
applyInitializers( context);
// 向各个监听器去发送容器准备好的事件
listeners. contextPrepared( context);
bootstrapContext. close( context);
if ( this. logStartupInfo) {
logStartupInfo( context. getParent() == null);
logStartupProfileInfo( context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context. getBeanFactory();
beanFactory. registerSingleton( "springApplicationArguments", applicationArguments);
if ( printedBanner != null) {
beanFactory. registerSingleton( "springBootBanner", printedBanner);
}
if ( beanFactory instanceof DefaultListableBeanFactory) {
(( DefaultListableBeanFactory) beanFactory)
. setAllowBeanDefinitionOverriding( this. allowBeanDefinitionOverriding);
}
if ( this. lazyInitialization) {
context. addBeanFactoryPostProcessor( new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 获取所有启动类
Set < Object > sources = getAllSources();
Assert. notEmpty( sources, "Sources must not be empty");
load( context, sources. toArray( new Object[ 0]));
listeners. contextLoaded( context);
}
  • 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.

Set<Object> sources = getAllSources();拿到启动类,我们来看下它是怎么拿到的?


         
         
public Set < Object > getAllSources() {
Set < Object > allSources = new LinkedHashSet <>();
// primarySources在SpringApplication的run方法中传入的
if ( ! CollectionUtils. isEmpty( this. primarySources)) {
allSources. addAll( this. primarySources);
}
if ( ! CollectionUtils. isEmpty( this. sources)) {
allSources. addAll( this. sources);
}
return Collections. unmodifiableSet( allSources);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

从源码中看到:它是将primarySources和sources两个集合合并后返回。因此primarySources和sources中存放的就是启动类。
下面看看它们在哪里被赋值的:
在SpringApplication的构造方法中,将构造方法中的参数直接添加到了primarySources集合中。


         
         
public SpringApplication( ResourceLoader resourceLoader, Class <?>... primarySources) {
this. resourceLoader = resourceLoader;
Assert. notNull( primarySources, "PrimarySources must not be null");
this. primarySources = new LinkedHashSet <>( Arrays. asList( primarySources));
this. webApplicationType = WebApplicationType. deduceFromClasspath();
this. bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
setInitializers(( Collection) getSpringFactoriesInstances( ApplicationContextInitializer. class));
setListeners(( Collection) getSpringFactoriesInstances( ApplicationListener. class));
this. mainApplicationClass = deduceMainApplicationClass();
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

另外有一个方法也涉及到往这个集合中添加值:


         
         
public void addPrimarySources( Collection < Class <?>> additionalPrimarySources) {
this. primarySources. addAll( additionalPrimarySources);
}
  • 1.
  • 2.
  • 3.

但是通过findUseage发现,在启动流程中,这个方法还没有被调用过。因此,当前状态下primarySources集合中只有SpringApplication.run(SpringBootDemoApplication.class, args);
参数中传入的启动类。

另外一个集合sources的只在一个方法中赋值:


         
         
public void setSources( Set < String > sources) {
Assert. notNull( sources, "Sources must not be null");
this. sources = new LinkedHashSet <>( sources);
}
  • 1.
  • 2.
  • 3.
  • 4.

这个方法只被SampleSpringXmlApplication类中的main方法调用,而Spring Boot的启动类是通过SpringApplication的run方法启动的。因此sources集合是空的。

Set<Object> sources = getAllSources();在Spring Boot工程启动过程中,拿到的是SpringBoot启动类,就是传入到SpringApplication.run()方法的第一个参数。

验证: 断点打在run方法


【Spring Boot源码剖析之Spring Boot源码剖析】_spring_11


primarySources 集合中只有一个SpringBootDemoApplication类对象


【Spring Boot源码剖析之Spring Boot源码剖析】_java_12


这个类对象正好是我们的启动类


【Spring Boot源码剖析之Spring Boot源码剖析】_java_13



断点打到getAllResource


【Spring Boot源码剖析之Spring Boot源码剖析】_java_14



allSources中只有我们的启动类对象,sources集合为空,验证了我们的结论。

【Spring Boot源码剖析之Spring Boot源码剖析】_java_15


接着看load()方法


         
         
/**
* Load beans into the application context.
* @param context the context to load beans into
* @param sources the sources to load
*/
protected void load( ApplicationContext context, Object[] sources) {
if ( logger. isDebugEnabled()) {
logger. debug( "Loading source " + StringUtils. arrayToCommaDelimitedString( sources));
}

// 创建BeanDefinitionLoader
BeanDefinitionLoader loader = createBeanDefinitionLoader( getBeanDefinitionRegistry( context), sources);
if ( this. beanNameGenerator != null) {
loader. setBeanNameGenerator( this. beanNameGenerator);
}
if ( this. resourceLoader != null) {
loader. setResourceLoader( this. resourceLoader);
}
if ( this. environment != null) {
loader. setEnvironment( this. environment);
}
loader. load();
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

在创建BeanDefinitionLoader之前,先获取BeanDefinitionRegistery:


         
         
private BeanDefinitionRegistry getBeanDefinitionRegistry( ApplicationContext context) {
if ( context instanceof BeanDefinitionRegistry) {
return ( BeanDefinitionRegistry) context;
}
if ( context instanceof AbstractApplicationContext) {
return ( BeanDefinitionRegistry) (( AbstractApplicationContext) context). getBeanFactory();
}
throw new IllegalStateException( "Could not locate BeanDefinitionRegistry");
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

其实就是将context中的IOC容器直接拿出来,并且转成BeanDefinitionRegistry。在前面看AnnotationConfigServletWebApplicationContext类继承关系的时候知道,它实现了BeanDefinitionRegistry接口,因此可以强转。并且可以验证这里就是AnnotationConfigServletWebApplicationContext。


【Spring Boot源码剖析之Spring Boot源码剖析】_java_16


BeanDefinitionRegistry定义了很重要的方法registerBeanDefinition(),该方法将BeanDefinition 注册进DefaultListableBeanFactory容器的beanDefinitionMap中。

现在再来看创建BeanDefinitionLoader的过程:


         
         
protected BeanDefinitionLoader createBeanDefinitionLoader( BeanDefinitionRegistry registry, Object[] sources) {
return new BeanDefinitionLoader( registry, sources);
}
  • 1.
  • 2.
  • 3.

构造方法:


         
         
/**
* Create a new {@link BeanDefinitionLoader} that will load beans into the specified
* {@link BeanDefinitionRegistry}.
* @param registry the bean definition registry that will contain the loaded beans
* @param sources the bean sources
*/
BeanDefinitionLoader( BeanDefinitionRegistry registry, Object... sources) {
Assert. notNull( registry, "Registry must not be null");
Assert. notEmpty( sources, "Sources must not be empty");
this. sources = sources;
//注解形式的Bean定义读取器 比如:@Configuration @Bean @Component @Controller @Service等等
this. annotatedReader = new AnnotatedBeanDefinitionReader( registry);
//XML形式的Bean定义读取器
this. xmlReader = ( XML_ENABLED ? new XmlBeanDefinitionReader( registry) : null);
this. groovyReader = ( isGroovyPresent() ? new GroovyBeanDefinitionReader( registry) : null);
//类路径扫描器
this. scanner = new ClassPathBeanDefinitionScanner( registry);
//扫描器添加排除过滤器
this. scanner. addExcludeFilter( new ClassExcludeFilter( sources));
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

在该方法中创建了reader和scanner,这两个很熟悉的东西,后面肯定会使用reader去读取配置,scaner进行包扫描。

接着看loader.load();方法:


         
         
/**
* Load the sources into the reader.
*/
void load() {
// 遍历启动类,分别进行load
for ( Object source : this. sources) {
load( source);
}
}

private void load( Object source) {
Assert. notNull( source, "Source must not be null");
// 从calss加载
if ( source instanceof Class <?>) {
load(( Class <?>) source);
return;
}
// 从resource加载
if ( source instanceof Resource) {
load(( Resource) source);
return;
}
// 从package加载
if ( source instanceof Package) {
load(( Package) source);
return;
}
// 从CharSequence加载
if ( source instanceof CharSequence) {
load(( CharSequence) source);
return;
}
throw new IllegalArgumentException( "Invalid source type " + source. getClass());
}
  • 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.

Spring Boot应用启动时会从class加载。


         
         
private void load( Class <?> source) {
if ( isGroovyPresent() && GroovyBeanDefinitionSource. class. isAssignableFrom( source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils. instantiateClass( source, GroovyBeanDefinitionSource. class);
(( GroovyBeanDefinitionReader) this. groovyReader). beans( loader. getBeans());
}

// 判断启动类是否符合注册条件
if ( isEligible( source)) {
this. annotatedReader. register( source);
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

什么是符合注册条件的:


         
         
private boolean isEligible( Class <?> type) {
// 不是匿名的,就是没有名字的类。不是groovy闭包。不是没有构造方法的
return !( type. isAnonymousClass() || isGroovyClosure( type) || hasNoConstructors( type));
}
  • 1.
  • 2.
  • 3.
  • 4.

这里,启动类是符合注册条件的。
所以启动类会被注册到IoC容器中。


         
         
this. annotatedReader. register( source);
  • 1.

而annotatedReader是BeanDefinitionLoader构造函数中被实例化的,并且它是Spring Framework中的类,所以注册过程是由Spring Framework完成的


         
         
BeanDefinitionLoader( BeanDefinitionRegistry registry, Object... sources) {
Assert. notNull( registry, "Registry must not be null");
Assert. notEmpty( sources, "Sources must not be empty");
this. sources = sources;
// AnnotatedBeanDefinitionReader是Spring Framework中的组件
this. annotatedReader = new AnnotatedBeanDefinitionReader( registry);
this. xmlReader = ( XML_ENABLED ? new XmlBeanDefinitionReader( registry) : null);
this. groovyReader = ( isGroovyPresent() ? new GroovyBeanDefinitionReader( registry) : null);
this. scanner = new ClassPathBeanDefinitionScanner( registry);
this. scanner. addExcludeFilter( new ClassExcludeFilter( sources));
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

刷新应用上下文

刷新上下文代码:


         
         
private void refreshContext( ConfigurableApplicationContext context) {
if ( this. registerShutdownHook) {
shutdownHook. registerApplicationContext( context);
}
// 核心方法
refresh( context);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

从源码可以看到,刷新上下文的核心方法是refresh方法


         
         
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh( ConfigurableApplicationContext applicationContext) {
applicationContext. refresh();
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

直接直接调用了application的refresh方法


         
         
public void refresh() throws BeansException, IllegalStateException {
synchronized( this. startupShutdownMonitor) {
StartupStep contextRefresh = this. applicationStartup. start( "spring.context.refresh");
// 第⼀步:刷新前的预处理
this. prepareRefresh();
/*
第⼆步:
获取BeanFactory;默认实现是DefaultListableBeanFactory
加载BeanDefition 并注册到 BeanDefitionRegistry
*/
ConfigurableListableBeanFactory beanFactory = this. obtainFreshBeanFactory();
// 第三步:BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加载器等)
this. prepareBeanFactory( beanFactory);

try {
// 第四步:BeanFactory准备⼯作完成后进⾏的后置处理⼯作
this. postProcessBeanFactory( beanFactory);
StartupStep beanPostProcess = this. applicationStartup. start( "spring.context.beans.post-process");
// 第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
this. invokeBeanFactoryPostProcessors( beanFactory);
// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执⾏
this. registerBeanPostProcessors( beanFactory);
beanPostProcess. end();
// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
this. initMessageSource();
// 第⼋步:初始化事件派发器
this. initApplicationEventMulticaster();
// 第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
this. onRefresh();
// 第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
this. registerListeners();
/*
第⼗⼀步:
初始化所有剩下的⾮懒加载的单例bean
初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
填充属性
初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
*/
this. finishBeanFactoryInitialization( beanFactory);
/*
第⼗⼆步:
完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事
件 (ContextRefreshedEvent)
*/
this. finishRefresh();
} catch ( BeansException var10) {
if ( this. logger. isWarnEnabled()) {
this. logger. warn( "Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}

this. destroyBeans();
this. cancelRefresh( var10);
throw var10;
} finally {
this. resetCommonCaches();
contextRefresh. end();
}

}
}
  • 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.

invokeBeanFactoryPostProcessors(beanFactory);(重点)

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

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

3、第三个过程:注册BeanDefinition
这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入
过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容
器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持
有这些BeanDefinition数据的。比如DefaultListableBeanFactory 中的beanDefinitionMap属性。
OK,总结完了,接下来我们通过代码看看具体是怎么实现的。

刷新应用上下文后的扩展接口


         
         
/**
* Called after the context has been refreshed.
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh( ConfigurableApplicationContext context, ApplicationArguments args) {
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理。