Spring Boot启动流程
SpringApplication 启动类
通过查看 SpringApplication.run() 方法的源码可以看到,该方法首先生成 SpringApplication 的一个实例,然后调用实例的 run() 方法。下面来看一下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 = new HashSet(); this.isCustomEnvironment = false; this.lazyInitialization = false; //② this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); // ③ this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.calss)); //④ this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); //⑤ this.mainApplicationClass = this.deduceMainApplicationClass(); }
- 注释①:打印启动信息。
- 注释②:表示 Bean 是否要以懒加载的方式进行实例化。
- 注释③:初始化 WebApplicationType 的类型,主要包括 REACTIVE、SERVLET 和 NONE 三种类型。
- 注释④:加载 ApplicationContextInitializer 类。
- 注释⑤:加载 ApplicationListener 类。
- 注释④和⑤是加载 META-INF/spring.factories 文件中配置的 ApplicationContext-Initializer 和 ApplicationListener 类。
具体配置代码如下:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplication
ContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplication
ContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplication
ContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplication
ContextInitializer
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPost
Processor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplication
Listener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplication
Listener
接下来看重要的部分,即 SpringApplication 实例的 run() 方法,具体源代码如下:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters =new ArrayList(); this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); //① listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); //② this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); //③ exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners,applicationArguments, printedBanner); //④ this.refreshContext(context); //⑤ 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, exceptionReporters,listeners); throw new IllegalStateException(var10); } try { listeners.running(context); //⑧ return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters,(SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }
- 注释①:初始化监听器,并开启监听器进行事件监听。
- 注释②:准备上下文环境,包括运行机器的环境变量、应用程序启动的变量和配置文件的变量等。初始化上下文环境后,启动监听器listeners.environment-Prepared(environment)。
- 注释③:初始化应用上下文。根据 WebApplicationType 类型的不同,生成的上下文也不同。如果是 SERVLET,则对应生成AnnotationConfigServletWebServer-ApplicationContext;如果是 REACTIVE,则对应生成 AnnotationConfigReactive-WebServerApplicationContext。默认生成 AnnotationConfigApplicationContext。
- 注释④:刷新应用上下文的准备工作。此处主要用于设置容器环境,启动监听器 listeners.contextPrepared(context),加载启动类,并调用listeners.contextLoaded (context) 方法。
- 注释⑤:刷新应用上下文,主要用于进行自动化装配和初始化 IoC 容器。
- 注释⑥:容器启动事件。
- 注释⑦:如果有用户定义的 CommandLineRunner 或者 ApplicationRunner,则遍历执行它们。
- 注释⑧:容器运行事件。
通过分析源代码,总结出 Spring Boot 启动的主要流程如图 1 所示。

图1 Spring Boot的启动流程
通过分析源代码还可以发现,事件监听是 Spring 框架中重要的一部分。Spring 提供了多种类型的事件,常用的如表 1 所示。
事 件 | 说 明 |
---|---|
ApplicationStartingEvent | 应用启动事件 |
ApplicationEnvironmentPreparedEvent | 环境准备事件 |
ApplicationContextInitializedEvent | 上下文初始化事件 |
ApplicationPreparedEvent | 应用上下文准备时触发事件 |
ApplicationStartedEvent | 应用启动事件,在刷新上下文之后触发 |
ApplicationReadyEvent | 应用上下文准备完成时触发事件 |
ApplicationFailedEvent | 应用启动失败事件 |
WebServerInitializedEvent | WebServer初始化事件 |
ContextRefreshedEvent | 上下文刷新事件 |
@SpringBootApplication注解
在 Spring Boot 的入口类中,有一个重要的注解 @SpringBootApplication,下面我们就对该注解进行分析。首先查看 @SpringBootApplication 的源代码,具体如下: @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfiguration ExcludeFilter.class) }) public @interface SpringBootApplication { @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true; }通过源码可以看到,@SpringBootApplication 是一个复合注解,包括 @ComponentScan、@EnableAutoConfiguration 和@SpringBootConfiguration 等。
下面具体分析这 3 个注解:
1) @ComponentScan注解
在 Bean 的注解,如@Service、@Repository、@Component 和 @Controller 等。@ComponentScan 注解可以自动扫描被以上注解描述的 Bean 并将其加载到 IoC 容器中。@ComponentScan 注解还有许多属性,通过这些属性可以更准确地指定哪些 Bean 被扫描并注入。- basePackages:指定需要扫描的包路径。
- basePackageClasses:指定类,并扫描该类所在包下的所有组件。
- includeFilters:指定哪些类可以通过扫描。
- excludeFilters:指定哪些类不被扫描。
- lazyInit:指定扫描的对象是否要懒加载。
- resourcePattern:指定符合条件的类文件。
2) @EnableAutoConfiguration注解
@EnableAutoConfiguration 注解是 Spring Boot 实现自动化配置加载的核心注解。通过 @Import 注入一个名为 AutoConfigurationImportSelector 的类,Spring-FactoriesLoader 类加载类路径下的 META-INF/spring.factories 文件来实现自动配置加载的过程。其中,spring.factories 文件配置了org.springframework.boot.autoconfigure. EnableAutoConfiguration 属性值,可以加载配置了 @Configuration 注解的类到 IoC 容器中。3) @SpringBootConfiguration注解
@SpringBootConfiguration 注解的功能类似于 @Configuration 注解,声明当前类是一个配置类,它会将当前类中有 @Bean 注解的实例加载到 IoC 容器中。声明:《Java系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。