spring-boot启动流程分析

前言

之前在工程实践中,我曾经尝试过利用 spring-boot 启动过程中提供的扩展点来优化业务的工程结构和部署效率,但是当时并未深入了解 spring-boot 的整体启动流程,今天看了看,大致了解了。

正文

我是通过调试一个 demo 工程了解的,工程结构为:

几个类的代码:

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
package io.github.since1986.learn.boot;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class Main {

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

@Component
public static class TestApplicationRunner implements ApplicationRunner {

@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("TestApplicationRunner");
}
}

@Component
public static class TestCommandLineRunner implements CommandLineRunner {

@Override
public void run(String... args) throws Exception {
System.out.println("TestCommandLineRunner");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package io.github.since1986.learn.boot;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

public class TestApplicationContextInitializer implements ApplicationContextInitializer {

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("TestApplicationContextInitializer");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package io.github.since1986.learn.boot;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

public class TestApplicationListener implements ApplicationListener {

@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("TestApplicationListener");
}
}
1
2
3
4
5
6
7
8
package io.github.since1986.learn.boot;

public class TestEnableAutoConfiguration {

public TestEnableAutoConfiguration() {
System.out.println("TestEnableAutoConfiguration");
}
}
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
package io.github.since1986.learn.boot;

import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

public class TestSpringApplicationRunListener implements SpringApplicationRunListener {

@Override
public void starting() {
System.out.println(TestSpringApplicationRunListener.class.getName() + "starting");
}

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println(TestSpringApplicationRunListener.class.getName() + "environmentPrepared");
}

@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println(TestSpringApplicationRunListener.class.getName() + "contextPrepared");
}

@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println(TestSpringApplicationRunListener.class.getName() + "contextLoaded");
}

@Override
public void started(ConfigurableApplicationContext context) {
System.out.println(TestSpringApplicationRunListener.class.getName() + "started");
}

@Override
public void running(ConfigurableApplicationContext context) {
System.out.println(TestSpringApplicationRunListener.class.getName() + "running");
}

@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println(TestSpringApplicationRunListener.class.getName() + "failed");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package io.github.since1986.learn.boot;

import org.springframework.boot.SpringBootExceptionReporter;

public class TestSpringBootExceptionReporter implements SpringBootExceptionReporter {

@Override
public boolean reportException(Throwable failure) {
System.out.println("TestSpringBootExceptionReporter");
return false;
}
}
1
2
3
4
5
6
# src/main/resources/META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=io.github.since1986.learn.boot.TestApplicationContextInitializer
org.springframework.context.ApplicationListener=io.github.since1986.learn.boot.TestApplicationListener
#org.springframework.boot.SpringApplicationRunListener=io.github.since1986.learn.boot.TestSpringApplicationRunListener
#org.springframework.boot.SpringBootExceptionReporter=io.github.since1986.learn.boot.TestSpringBootExceptionReporter
org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.github.since1986.learn.boot.TestEnableAutoConfiguration

下面开始分析:

概念

SpringApplication 整个spring-boot启动流程的入口点

ApplicationContextInitializer 用于干预ApplicationContext的启动过程,也就是一个ApplicationContext的扩展点

SpringFactoriesLoader 用SPI机制从特定的配置文件(spring.factories)加载可用的ApplicationContextInitializer实现类名等配置信息

设计

SPI (通过SpringFactoriesLoader 实现)

实现

  • 第一步 new SpringApplication() 也就是执行SpringApplication的构造器

    • WebApplicationType.deduceFromClasspath(); 推断应用类型
    • setInitializers 设置ApplicationContextInitializer
      • SpringFactoriesLoader.loadFactoryNames(type, classLoader) 用来获得所有可用的ApplicationContextInitializer的类的names
      • createSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set names) 创建所有可用的ApplicationContextInitializer的类的实例
    • setListeners 设置ApplicationListener
      • SpringFactoriesLoader.loadFactoryNames(type, classLoader) 用来获得所有可用的ApplicationListener的类的names
      • createSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set names) 创建所有可用的ApplicationListener的类的实例
    • deduceMainApplicationClass(); 推断 main() 所在的类
  • 第二步 执行public ConfigurableApplicationContext run(String… args) 创建ApplicationContext并且refresh()

    • SpringApplicationRunListeners getRunListeners(String[] args) 获得SpringApplicationRunListeners
      • SpringFactoriesLoader.loadFactoryNames(type, classLoader) 用来获得所有可用的SpringApplicationRunListener的类的names
      • createSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set names) 创建所有可用的SpringApplicationRunListener的类的实例
      • prepareEnvironment(listeners, applicationArguments); 准备环境信息
      • printBanner(environment); 打印 Banner,也就是控制台上的 ASCII art
      • ConfigurableApplicationContext createApplicationContext() 最关键的一步,创建ApplicationContext
        • 根据第一步中推断出来的应用类型确定要创建的ApplicationContext的类型 contextClass
        • 根据 contextClass 调用 BeanUtils.instantiateClass(contextClass); 创建ApplicationContext实例
      • 构建异常报告器 exceptionReporters
      • prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner rintedBanner) 准备ApplicationContext
        • context.setEnvironment(environment); 将之前准备好的环境信息设置到 ApplicationContext
        • postProcessApplicationContext(context);
        • applyInitializers(ConfigurableApplicationContext context) 将ApplicationContext实例传入到之前实例化好的所有ApplicationContextInitializer并调用initialize来完成干预ApplicationContext的创建
        • 这之间有多个地点会触发之前实例化好的 SpringApplicationRunListener 从而发布相应的消息
      • refreshContext(context); refresh ApplicationContext (context.registerShutdownHook(); 如果需要还会注册 ShutdownHook)
      • afterRefresh(context, applicationArguments); 执行ApplicationContext刷新后回调,这个也是个简易的扩展点
      • listeners.started(context); 将ApplicationContext传给之前实例化好的所有SpringApplicationRunListener 并启动(也就是说 SpringApplicationRunListener 是在 ApplicationContext 刷新后触发的)
      • callRunners(ApplicationContext context, ApplicationArguments args)
        • 从已经 refresh 好的 ApplicationContext 中获得所有之前实例化好的 ApplicationRunner 和 CommandLineRunner 的 Bean
        • 对这些Runner进行排序
        • 触发这些Runner运行
      • handleRunFailure(context, ex, exceptionReporters, null); 如果在以上过程中出现异常了则会用之前实例化好的异常报告器报告异常

注意,还有一个隐藏的重要流程,也就是spring-boot自动配置的流程:

  • 在上面流程中的refresh ApplicationContext中会执行org.springframework.context.annotation.ConfigurationClassParser#processImports 这个方法会处理内嵌在spring-boot根注解@SpringBootApplication中的 @EnableAutoConfiguration 注解中的 @Import(AutoConfigurationImportSelector.class),实际上也就是运行 AutoConfigurationImportSelector 当中的流程。
    • 在 AutoConfigurationImportSelector 的流程中有这样的一个方法 org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
      • getCandidateConfigurations(annotationMetadata, attributes); 这个方法内部仍旧是使用SpringFactoriesLoader来获得所有@EnableAutoConfiguration对应的spring-boot的自动配置注解AutoConfiguration:
1
io.github.since1986.learn.boot.TestEnableAutoConfiguration, org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration, org.springframework.boot.autoconfigure.aop.AopAutoConfiguration, org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration, org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration, org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration, org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration, org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration, org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration, org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration, org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration, org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration, org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration, org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration, org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration, org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration, org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration, org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration, org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration, org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration, org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration, org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration, org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration, org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration, org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration, org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration, org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration, org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration, org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration, org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration, org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration, org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration, org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration, org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration, org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration, org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration, org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration, org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration, org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration, org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration, org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration, org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration, org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration, org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration, org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration, org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration, org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration, org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration, org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration, org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration, org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration, org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration, org.springframework.boot.autoconfigure.session.SessionAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration, org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration, org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration, org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration, org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration, org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration, org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration, org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration, org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration, org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration, org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration, org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration, org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration, org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration, org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration, org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration, org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration, org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration, org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

总结

  • 在整体流程中多次使用了SPI机制,也就是SpringFactoriesLoader 来获取需要的外部组件的类型名称
  • 在整体流程中多次使用了事件监听器,也就是观察者模式