Spring源码解析


spring run启动流程

前言

​ 写项目的时候不想知道Run()方法里面都干啥了么?那些Bean还有各种配置啥时候加载进去的?现在看来看一下。

​ 一个SpringBoot项目的根目录中最重要的一个类就是Application.java,各自有各自的起名习惯,最主要的是这个类中标注了一个注解那就是@SpringBootApplication,随后最重要的就是main方法了。

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

Run()方法

  • 点进去Run()方法看一下源码:
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
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}

/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}

/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//1、先实例化SpringApplication对象
//2、再执行run()方法
return new SpringApplication(primarySources).run(args);
}

看到最后一行,一步一步看一下源码:

1、实例化SpringApplication对象

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

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//获取类加载器,但是这里传进来的是null,所以现在还没有实例化Bean的类加载器
this.resourceLoader = resourceLoader;
//断言判空校验
Assert.notNull(primarySources, "PrimarySources must not be null");
//将Application.class(你自己创建的main方法的类加载到主资源集合中)
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//1、加载容器,这里就相当于一个筛选器,加载不同的Bean需要不同的容器
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//2、这里就开始使用IOC了,加载META-INF/spring.factories中的初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//3、加载META-INF/spring.factories中的监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//4、加载主类
this.mainApplicationClass = deduceMainApplicationClass();
}

再根据上述代码一步一步看一下流程:

1、加载容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
// 默认servlet处理器
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

// reactive处理器
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

static WebApplicationType deduceFromClasspath() {
// 如果不存在reactive处理器,并且存在servlet,则返回reactive
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}

​ 使用ClassUtils.isPresent()对这三个类进行判断,看能不能加载,相当于一个过滤器,不然这里怎么会写出分叉逻辑,可以返回三个容器类型。

2、装配初始化器

​ 了解 SpringBoot自动装配知道有两处有spring.factories,这里加载的是

spring_factories

ApplicationContextInitializer

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
// 获取spring工厂实例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
// 获取spring工厂实例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 获取加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 使用名称并确保其唯一性,以防止重复
// 从"META-INF/spring.factories"加载指定类型的类全路径
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建spring工厂实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
// 获取对象class类型
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
// 获取构造方法
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 初始化对象
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
3、装配监听器

ApplicationListener

调用方式和上一步一致

4、加载主类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
this.mainApplicationClass = deduceMainApplicationClass();

private Class<?> deduceMainApplicationClass() {
try {
//获取栈堆的信息
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
//循环找出main方法
if ("main".equals(stackTraceElement.getMethodName())) {
//加载main方法的类
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}

到这儿,实例化SpringApplication对象结束,开始执行run()方法。

2、执行Run()方法

​ 看一下源码,这段有点长,但是没事,一步一步来

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
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
// 计时器启动,我们总能看到项目启动时间多少,就是它统计出来的
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

// 1、设置系统headless为true
configureHeadlessProperty();
// 2、加载SpringApplicationListener和上面初始化加载可不一样啊,上面的是ApplicationListener
SpringApplicationRunListeners listeners = getRunListeners(args);
// 系统启动前-的监听,这个可以实现ApplicationListener<ApplicationStartingEvent>来自定义监听器
listeners.starting();
try {
// 初始化参数对象,args就是main方法里的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境变量和各种默认的转换器和格式器,意思就是把电脑中的环境变量加载进来
// 还有我们常用的那些日期格式,字符格式转换的类加载进来,所以说这一步做的还挺多
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// spring.beaninfo.ignore 的配置用来决定是否跳过 BeanInfo 类的扫描,如果设置为 true,则跳过。
configureIgnoreBeanInfo(environment);
// 3、这个就是系统启动时出现的“SPRINGBOOT”图形,这个可以看下源码
Banner printedBanner = printBanner(environment);
// 创建AnnotationConfigReactiveWebServerApplicationContext,这块儿有些复杂,先不说
context = createApplicationContext();
// 4、异常报告类加载
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 5、准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 6、刷新上下文
refreshContext(context);
// 刷新后的操作
afterRefresh(context, applicationArguments);
// 结束计时
stopWatch.stop();
// 打印启动时间
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 7、系统上下文刷新完成后的监听器
listeners.started(context);
// 8、执行自定义run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
//系统启动完成运行时的监听器
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
1、设置headless

​ 看一下这个方法干啥的,源码:

1
2
3
4
5
6
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";

private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

看到把这个全局静态变量设置为ture,那么“java.awt.headless”这个是干什么的?

这个属性是用来禁用图形功能。在一些服务器环境或无图形界面的操作系统上,启用图形功能可能会导致不必要的资源消耗和性能下降。因此,将 java.awt.headless 属性设置为 true 可以使得 Java 应用程序可以在无图形界面的环境中运行,降低功耗。

除了上面这种启动方式,也可以在application.yml中这样设置打开:

1
2
3
java:
awt:
headless: true
2、启用SpringApplicationListener
1
2
3
4
5
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
3、加载Banner
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 Banner printBanner(ConfigurableEnvironment environment) {
......
if (this.bannerMode == Mode.LOG) {
//这里就是加载Banner的方法
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

public Banner print(Environment environment, Class<?> sourceClass, Log logger) {

//获取Banner
Banner banner = getBanner(environment);
......
return new PrintedBanner(banner, sourceClass);
}

private Banner getBanner(Environment environment) {
Banners banners = new Banners();

//1、可以放图片Banner哦!!
banners.addIfNotNull(getImageBanner(environment));
//2、文字的Banner
banners.addIfNotNull(getTextBanner(environment));
......
}
3.1、图片Banner
1
2
3
4
5
6
7
8
9
10
11
12
13
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };

private Banner getImageBanner(Environment environment) {
String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
......
for (String ext : IMAGE_EXTENSION) {
Resource resource = this.resourceLoader.getResource("banner." + ext);
if (resource.exists()) {
return new ImageBanner(resource);
}
}
}

spring.banner.image.location属性用于指定自定义启动横幅(Banner)的图片文件位置。

  • 1、可以将自定义的启动横幅图片放置在应用程序的src/main/resources/目录下,但必须是”gif”, “jpg”, “png” 中的一种:
    src/main/resources/banner.png

  • 2、 也可以放在yml文件中:

    1
    2
    3
    4
    5
    6
    # application.yml

    spring:
    banner:
    image:
    location: classpath:banner.png

​ 这样,在启动 Spring Boot 应用程序时,控制台将显示指定位置的 banner.png 图片作为启动横幅,而不是默认的文本横幅。请确保图片文件存在,并且格式正确,以便正确显示在控制台中。

3.2、文本Banner
1
2
3
4
5
6
7
8
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";

private Banner getTextBanner(Environment environment) {
String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
DEFAULT_BANNER_LOCATION);
......
}

spring.banner.location属性用于指定自定义启动横幅(Banner)的文本文件位置。

  • 1、可以将自定义的启动横幅图片放置在应用程序的src/main/resources/目录下:
    src/main/resources/custom-banner.txt

  • 2、 也可以放在yml文件中:

    1
    2
    3
    4
    5
    6
    # application.yml

    spring:
    banner:
    location: classpath:custom-banner.txt
    banner.txt这个则是默认的文本,和上面的使用方式一样,只不过文件名字是:banner.txt。
4、异常报告类加载

​ 加载spring.factories中的Erro Reporters

5、准备上下文
1
2
3
4
5
6
7
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
......
}

让我们逐行解释这段代码:

1、context.setEnvironment(environment);:设置应用程序的上下文环境。environment 包含了应用程序的配置信息和属性。

2、postProcessApplicationContext(context);:对应用程序的上下文进行后处理。这里可以进行一些定制化的操作,例如注册 Bean 或者添加自定义的配置。

3、applyInitializers(context);:应用初始化器。上文我们说的加载的初始化器可以在此处被调用。

4、listeners.contextPrepared(context);:通知所有的应用程序监听器,上下文已经被准备好了。这些监听器可以在应用程序的不同运行阶段做出操作。

5、刷新上下文
应用程序上下文会经历一系列的初始化和刷新过程,包括 Bean 的实例化、依赖注入、后置处理器的应用、事件发布等。这是整个应用程序上下文初始化的核心步骤,确保应用程序上下文得到正确初始化和配置。

6、系统上下文刷新完成后的监听器
listeners.started(context) 是用来通知应用程序启动监听器(ApplicationStartedEvent 监听器)的一个方法调用。它表示应用程序的上下文已经启动并刷新完成,在此时会触发 ApplicationStartedEvent 事件。

ApplicationStartedEvent 事件是一种通知机制,用于在系统特定时间点广播事件,允许其他组件或代码对这些事件进行响应。例如初始化某些组件、执行定时任务、发送通知等。我们自己也可以编写一个实现了 ApplicationListener<ApplicationStartedEvent> 接口的监听器,并在其中实现相应的逻辑。

以下是一个简单的示例,展示如何创建一个应用程序启动监听器并处理 ApplicationStartedEvent 事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationStartedListener implements ApplicationListener<ApplicationStartedEvent> {

@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 在应用程序启动后执行一些逻辑
System.out.println("Application is started. Do something here...");
}

}

​ 我创建了一个名为 MyApplicationStartedListener 的监听器,并实现了 ApplicationListener<ApplicationStartedEvent> 接口。在 onApplicationEvent 方法中,我们可以编写需要在应用程序启动后执行的逻辑。

当应用程序上下文启动并刷新完成时,Spring Boot 会自动触发 ApplicationStartedEvent 事件,而 MyApplicationStartedListener 监听器将接收到这个事件,并执行其中的逻辑。

8、执行自定义run方法
callRunners(context, applicationArguments)是用来调用应用程序的 ApplicationRunnerCommandLineRunner 的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
//获取所有实现ApplicationRunner接口的类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
//获取所有实现CommandLineRunner接口的类
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}

​ 实现ApplicationRunnerCommandLineRunner 两个接口,用于在 Spring Boot 启动完成后执行一些自定义操作。例如加载初始化数据、执行定时任务、配置某些组件等。

​ 但是这两个接口的作用略有不同:
ApplicationRunner 接口:定义了一个 run 方法,该方法在应用程序启动后被调用,传递一个 ApplicationArguments 对象,用于接收命令行参数。
CommandLineRunner 接口:也定义了一个 run 方法,该方法在应用程序启动后被调用,传递一个 String 数组,用于接收命令行参数。

​ 在上述代码中,通过

1
2
3
context.getBeansOfType(ApplicationRunner.class)

context.getBeansOfType(CommandLineRunner.class)
分别获取了所有实现的 `ApplicationRunner` 和 `CommandLineRunner` 的类,然后对类进行排序,按照顺序执行这些实例的 `run` 方法。

下面举个例子:

假设有一个简单的学生管理系统。在应用程序启动时,我们希望自动向数据库中插入一些初始化的学生数据,并打印出命令行参数。

首先,我们需要创建一个学生实体类和一个学生数据初始化服务类。

1、学生实体类 Student.java

1
2
3
4
5
6
7
public class Student {
private Long id;
private String name;
private int age;

// 省略构造函数、getter 和 setter 方法
}

2、学生数据初始化服务类 StudentDataService.java

1
2
3
4
5
6
7
8
@Service
public class StudentDataService {
public void initializeData() {
// 在这里执行初始化数据的逻辑,将学生数据插入数据库
// 简化起见,这里不连接真实数据库,只在控制台打印初始化信息
System.out.println("Initializing student data...");
}
}

​ 接下来,我们需要创建一个实现 ApplicationRunner 接口的初始化器,或者一个实现 CommandLineRunner 接口的初始化器。这里我都创建出来看一下,这两个初始化器会在应用程序启动时被调用,并执行相应的逻辑。

  1. 实现 ApplicationRunner 的初始化器 StudentDataApplicationRunner.java
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class StudentDataApplicationRunner implements ApplicationRunner {
@Autowired
private StudentDataService studentDataService;

@Override
public void run(ApplicationArguments args) throws Exception {
// 在应用程序启动后执行的逻辑,插入学生数据
studentDataService.initializeData();
}

}
  1. 实现 CommandLineRunner 的初始化器 CommandLineAppRunner.java
1
2
3
4
5
6
7
8
@Component
public class CommandLineAppRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 在应用程序启动后执行的逻辑,打印命令行参数
System.out.println("Command line arguments: " + Arrays.toString(args));
}
}

​ 现在,当应用程序启动后,StudentDataApplicationRunnerCommandLineAppRunner 这两个初始化器会自动被调用。StudentDataApplicationRunner 会执行 StudentDataService 中的初始化数据逻辑,而 CommandLineAppRunner 则会打印命令行参数。

9、监听器
上文第二大点“执行run()方法”中,如果仔细看,源码中提到了三个监听器分别是:

1
2
3
4
5
6
7
1、listeners.starting();

2、listeners.started(context);

3、listeners.running(context);

4、listeners.starting();

​ 是用来通知系统启动前的监听器(ApplicationStartingEvent 监听器)的一个方法调用。它表示系统即将开始启动,在此时会触发 ApplicationStartingEvent 事件。使用方法和上文第7小点一样哈,只不过要实现ApplicationListener接口。

2、listeners.started(context);
是用来通知系统启动监听器(ApplicationStartedEvent 监听器)的一个方法调用。它表示系统的上下文已经启动并刷新完成,在此时会触发 ApplicationStartedEvent 事件。使用方法和上文第7小点一样哈,只不过要实现ApplicationListener接口。

3、listeners.running(context);
是用来通知系统运行监听器(ApplicationRunningEvent 监听器)的一个方法调用。它表示系统的上下文已经启动并正在运行中,在此时会触发ApplicationRunningEvent事件。使用方法和上文第7小点一样哈,只不过要实现ApplicationListener接口。

这三个监听器一定要区分开,是不一样的哦,它们都是可以扩展的,我们在系统不同的启动阶段可以做不同的监听器做自定义骚操作!

总结
这里我们就分析完了Run()方法的运行流程,在这里面有很多的可以自定义扩展的,下面的这些:ApplicationContextInitializer、ApplicationListener、banner、exceptionReporter、ApplicationRunner、CommandLineRunner,还有listeners.starting()、listeners.started(context)、listeners.running(context),这些都是可以自定义进行个性化扩展的,对这些都熟悉后,自己去对SpringBoot启动优化时间、DIY监听器,都是可以做的,分析不易,有帮助的话点个赞!

1. 启动Spring容器

  • Spring容器启动的第一步是创建 ApplicationContext 对象。 AplicationContext
    是Spring容器的核心接口,代表了Spring上下文环境。常见的实现类有:
    • ClassPathXmlApplicationContext: 从类路径下的XML配置文件中加载Bean定义。
    • AnnotationConfigApplicationContext: 基于注解配置启动容器。
    • FileSystemXmlApplicationContext: 从文件系统中的XML配置文件中加载Bean定义。

2. 读取和解析配置文件

  • Spring需要根据配置(XML文件、注解配置类等)加载Bean定义。Spring的BeanDefinitionReader负责解析这些配置。

    • 如果使用XML配置文件,XmlBeanDefinitionReader负责读取和解析配置文件,并将解析得到的Bean定义信息转换为BeanDefinition对象。
    • 如果使用注解配置类,AnnotatedBeanDefinitionReader负责扫描和解析注解,并注册Bean。

3. BeanDefinition的注册

  • 解析Bean定义后,Spring会将每个Bean的定义信息以BeanDefinition形式注册到BeanFactory中。BeanDefinition包含了Bean的各种元数据,比如Bean的类类型、作用域(单例/原型)、是否需要懒加载、依赖关系等。
  • 通过DefaultListableBeanFactory来管理和存储这些BeanDefinition

4. Spring容器刷新(refresh())

  • 这是Spring启动过程中最核心的步骤。 ApplicationContextrefresh()

    方法会完成一系列操作,启动和初始化Spring容器:

    1. 准备上下文环境
      • 设置上下文环境,包括配置资源(例如属性文件的读取),初始化一些特定的环境变量。
    2. 初始化BeanFactory
      • 创建或刷新BeanFactory,即完成对所有BeanDefinition的解析和注册,形成一个完整的Bean定义列表。
    3. BeanFactory的后处理
      • 如果有BeanFactoryPostProcessor(比如PropertyPlaceholderConfigurer),Spring会在这个阶段执行它们的回调,修改BeanDefinition的定义或者其他元数据。
    4. 注册Bean后处理器(BeanPostProcessor)
      • 注册所有实现BeanPostProcessor接口的类。这些处理器允许在Bean初始化前后进行拦截处理(如AOP代理、依赖注入后的处理等)。
    5. 实例化和初始化单例Bean
      • 对所有非懒加载的单例Bean进行实例化和初始化。这里涉及到Bean的生命周期方法的调用,包括依赖注入、初始化方法的调用。
      • 初始化时会依次执行以下几个步骤:
        • 调用Bean的构造方法实例化。
        • 通过set方法注入依赖。
        • 如果Bean实现了BeanNameAwareBeanFactoryAware等接口,会调用相应的方法。
        • 如果有BeanPostProcessor,会在Bean初始化前后进行拦截处理。
        • 执行Bean的初始化方法(如@PostConstruct或自定义的init-method)。
    6. 初始化事件广播器(ApplicationEventMulticaster)
      • 创建并初始化事件广播器,用于处理应用事件,如ContextRefreshedEvent等。
    7. 注册事件监听器
      • 注册应用程序中的事件监听器(如果有ApplicationListener),使得它们能够监听Spring容器发布的事件。
    8. 完成容器的初始化
      • 发布ContextRefreshedEvent,通知所有监听器Spring容器已经完全启动。

5. 获取Bean

  • 当容器启动并完成初始化后,用户可以通过ApplicationContext.getBean()方法获取Bean的实例,使用已经准备好的Bean来处理业务逻辑。

6. 销毁与关闭容器

  • 当应用程序结束或者容器关闭时,Spring会调用所有单例Bean的销毁方法,通常通过close()destroy()来关闭容器。
  • 销毁过程包括:
    • 调用所有实现了DisposableBean接口的Bean的destroy()方法。
    • 如果配置了自定义的销毁方法(如destroy-method),也会被执行。
    • 发布ContextClosedEvent,通知监听器容器已经关闭。
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
+-------------------+
| 启动ApplicationContext |
+-------------------+

+-------------------+
| 读取和解析配置文件 |
+-------------------+

+-------------------+
| 注册BeanDefinition |
+-------------------+

+-------------------+
| 容器刷新(refresh) |
+-------------------+

| - 初始化环境变量 |
| - 初始化BeanFactory |
| - 执行BeanFactoryPostProcessor |
| - 注册BeanPostProcessor |
| - 实例化和初始化单例Bean |
| - 事件监听器注册 |
| - 发布ContextRefreshedEvent |
+-------------------+

+-------------------+
| 获取Bean并使用 |
+-------------------+

+-------------------+
| 容器销毁与关闭 |
+-------------------+

主要方法:

  • refresh():Spring容器的核心方法,负责完成Bean的创建、初始化、后处理等工作。
  • getBean():从Spring容器中获取Bean实例的主要接口。
  • close():关闭Spring容器,销毁Bean并清理资源。
refresh()
1
org.springframework.context.support.AbstractApplicationContext#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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

// Prepare this context for refreshing.
// 准备此上下文以进行刷新
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
// 告诉子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
// 准备bean工厂以在当前上下文使用
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
// 允许在上下文子类中对bean工厂进行后处理
postProcessBeanFactory(beanFactory);

StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
// 调用在上下文中注册为bean的工厂处理器
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
// 注册拦截bean创建的bean处理器
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();

// Initialize message source for this context.
// 为此上下文初始化消息源
initMessageSource();

// Initialize event multicaster for this context.
// 为此上下文初始化事件传播
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
// 在特定上下文子类中初始化其他特殊bean
onRefresh();

// Check for listener beans and register them.
// 检查监听器bean并注册它们
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
// 实例化所有剩余的(非懒惰初始化)单例
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
// 最后一步:发布相应的事件
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
// 销毁已创建的单例以避免悬挂资源
destroyBeans();

// Reset 'active' flag.
// 重置“活动”标志
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
// 重置Spring核心中的常见自省缓存,因为我们可能再也不需要单例bean的元数据了。。。
resetCommonCaches();
contextRefresh.end();
}
}
}
getBean()
1
org.springframework.beans.factory.support.AbstractBeanFactory#getBean
close()

总结:

Spring的启动流程围绕着容器的初始化和Bean的创建展开,通过读取配置文件、注册Bean定义、调用生命周期回调方法(如BeanPostProcessorBeanFactoryPostProcessor)、初始化Bean及事件驱动等多个步骤,最终构建出一个完整的应用上下文