Yihsi

Spring Boot 应用启动过程分析

对于 Spring Boot 应用来说,不管是直接运行调用了 SpringApplication#run(Class, String[])main 方法来启动 Spring Boot 应用,还是通过传统方式(参见 SpringBootServletInitializerSpringServletContainerInitializer),将应该打包成 war 包放到 Web 容器中去运行,最终都会调用 SpringApplication#run(String...) 方法来启动应用、创建和刷新 ApplicationContext

下面先看 SpringApplication#run(String...) 方法:

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
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 从 spring.factories 中加载 SpringApplicationRunListener,其中包括
// EventPublishingRunListener,它的 starting 方法会发布 ApplicationStartingEvent 事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 准备当前应用运行时所在的环境,会发布 ApplicationEnvironmentPreparedEvent 事件,也是由
// EventPublishingRunListener 发布
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备 context ,会发布 ApplicationPreparedEvent 事件,也是由
// EventPublishingRunListener 发布,会调用 SpringApplication 构造方法中加载的
// initializers 的 initialize 方法
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新 context ,完成应用的启动并且发布 ContextRefreshedEvent 事件
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// EventPublishingRunListener 在这里会发布 ApplicationReadyEvent 事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

上面调用的方法中最重要的是 refreshContext 方法,它最终会调用 AbstractApplicationContext#refresh() 方法,refresh 方法主要完成以下工作:

  • 通过调用 invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) 方法从入口类(即使用 @SpringBootApplication 注解的类,对于打包成 war 包放到 Web 容器运行的情况,入口类仅仅是个配置类)读取注解,处理 @ComponentScan@Import 注解( @SpringBootApplication 包含了 @ComponentScan 注解,也包含了 @EanbleAutoConfiguration 注解,此注解包含 @Import 注解), 对前者的处理会扫描并注册所有 @Component 注解的类的 bean 定义,如果是 @Configuration (此注解包含了 @Component 注解)注解的类,将会递归进行解析(注册其声明的 Bean 的定义);对于 @Import 注解,会调用注解 value 对应的类的 selectImports 方法。

  • 对于 @EanbleAutoConfiguration 来说, 其 @Import 注解 value 对应的类是 AutoConfigurationImportSelector ,它的 selectImports 方法会从 META-INF/spring.factories 加载很多符合条件的自动配置的类,类似于配置类,也会递归解析注册其声明的 Bean 定义。

  • 继续执行到 onRefresh() 方法, createWebServer() 将会被调用:

    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
    // ServletWebServerApplicationContext.java
    private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    // 使用内置容器启动的情况
    if (webServer == null && servletContext == null) {
    ServletWebServerFactory factory = getWebServerFactory();
    // 创建一个 Tomcat 实例并启动,会调用传入的 ServletContainerInitializer 的 onStartup 方
    // 法,这里 getSelfInitializer() 方法返回的 ServletContainerInitializer 是一个匿名内部
    // 类,其 onStartup 方法会调用 selfInitialize 方法, selfInitialize 方法会获取所有
    // ServletContainerInitializer 实例(比如 ServletRegistrationBean 和
    // FilterRegistrationBean 实例)并调用其 onStartup 方法。对于 ServletRegistrationBean
    // 和 FilterRegistrationBean 实例 来说,其 onStartup 方法会将对应的 Servlet 和 Filter
    // 添加到 ServletContext 中。
    this.webServer = factory.getWebServer(getSelfInitializer());
    }
    // 使用外部容器启动的情况
    else if (servletContext != null) {
    try {
    getSelfInitializer().onStartup(servletContext);
    }
    catch (ServletException ex) {
    throw new ApplicationContextException("Cannot initialize servlet context",
    ex);
    }
    }
    initPropertySources();
    }

    DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration#dispatcherServletRegistration() 返回的 DispatcherServletRegistrationBean 包含一个 DispatcherServlet 实例,在 onStartup 方法中会被添加到 ServletContext 中。

  • 接下来查找并注册监听器的 Bean 定义,实例化所有未实例化的(non-lazy-init)单例 Bean 。

  • 接着调用 finishRefresh() 方法,发布 ContextRefreshedEvent 事件。

  • 这个时候应用内置容器已经启动好了,应用也就运行起来了。