SpringBoot启动流程
启动类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
@SpringBootApplication
对于一个SpringBoot程序的启动首先需要一个加了@SpringBootApplication
注解的启动类。
@SpringBootApplication
本质上是一个复合注解由三个注解组成
@EnableAutoConfiguration
,有了它之后再启动时就会导入“自动配置”AutoConfigurationImportSelector
类,这个类会将所有复合条件的@Configuration
配置都进行加载。@SpringBootConfiguration
,等同于@Configuration
,就是将这个类标记为配置类,会被加载到容器中。@ComponentScan
,自动扫描并加载复合条件的Bean
如果启动类中不需要增加配置内容不需要指定扫描路径,可以用@EnableAutoConfiguration
替代@SpringBootApplication
。
SpringApplication.run(App.class,args);
注解完成后,我们运行的起点就是SpringApplication
的run
方法,再run
方法执行后会经历如下四个阶段
- 服务构建
- 环境准备
- 容器创建
- 填充容器
- 执行Runner
服务构建
服务构建指的服务就是SpringApplication
本身,这个阶段指的就是创建SpringApplication
对象。
-
首先需要把传入的资源加载器、主方法类记录再内存中
this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))
-
然后逐一判断对应的服务类是否存在来确定Web服务的类型,
Servlet
、Reactive
、None
,默认为Servlet。this.webApplicationType = WebApplicationType.deduceFromClasspath();
-
加载初始化类,通过读取所有
META-INF/spring.factories
文件中BootstrapRegistryInitializer
(注册初始化)、ApplicationContextInitializer
(上下文初始化)、ApplicationListener
(监听器)这三类配置。SpringBoot中没有默认的初始化配置,但是配置了7个上下文初始化和8个监听器。这些配置信息会在后续的启动中用到,我们也可以自定义这三个配置,只需要放到工程中的spring.factories
文件中。this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
-
判断
main
方法所在的类,大概率就是启动类本身。this.mainApplicationClass = deduceMainApplicationClass();
自此我们的Spring服务SpringApplication
就构造完成了。
环境准备
这个阶段的目的是给即将创建的容器配置好环境信息。
-
我们会先创建一个后续会用到的启动上下文
BootStrapContext
,同时逐一调用刚刚加载的"注册初始化器"BootstrapRegistryInitializer
中的initialize
方法。因为我们默认的没有BootstrapRegistryInitializer
所以并不执行什么。DefaultBootstrapContext bootstrapContext = createBootstrapContext(); private DefaultBootstrapContext createBootstrapContext() { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext; }
-
设置缺少显示器、键盘等输入设备也可以正常启动
configureHeadlessProperty(); private void configureHeadlessProperty() { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); }
-
获取并启动'运行监听器'
SpringApplicationRunListeners
,同时发布启动事件。它获取并加载SpringBoot工程中的spirng.factories
配置文件中的EventPublishingRunListener
,它在启动时也会将刚刚我们说的8个ApplicationListener
都进行引入。SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup); }
-
通过prepareEnvironment方法组装启动参数。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { //第一步 ConfigurableEnvironment environment = getOrCreateEnvironment(); //第二步 configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
- 根据不同的Web环境构建不同的环境 ,默认
Servlet
。 - 加载
systemEnvironment(系统环境变量,比如JAVA_HOME、PATH)
、systemProperties(jvm系统属性比如java.vm.version、file.encoding)
等在内的4组配置信息。把这些配置信息都加载到一个叫做propertySources
的内存集合中,方便后续使用就无需重新加载。 - 通过
configureEnvironment(配置环境)
将我们传入的环境参数args
进行设置。发布环境准备完成事件
- 根据不同的Web环境构建不同的环境 ,默认
-
将
spring.beaninfo.ignore
设置为true,表示不加载Bean的元数据信息,同时打印Banner图
容器创建
我们通过createApplicationContext
来创建容器,首先根据服务类型创建ConfigurableApplicationContext
,默认的服务类型是servlet
,所以创建的AnnotationConfigServletWebServerApplicationContext
,在这个过程中会构造诸如:
- 存放和生产我们Bean实例的Bean工厂
DefaultListableBeanFactory
。 - 用来解析
@Component、@ComponentScan
等注解的配置类后处理器ConfigurationClassPostProcessor
。 - 用来解析
@Autowired、@Value、@Inject
等注解的Bean后处理器AutowiredAnnotationBeanPostProcessor
等在内的属性对象。
接着通过prepareContext
方法对容器中的部分属性进行初始化
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
// 第一步
postProcessApplicationContext(context);
// 第二步
applyInitializers(context);
// 第三步
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();
// 第四步
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
- 先用
postProcessApplicationContext
方法设置Bean名称生成器、资源加载器、类型转换器。 - 接着执行之前我们加载进来的上下文初始化
ApplicationContextInitializer
。 - 为容器注册启动参数、Banner、Bean引用策略和懒加载等。
- 通过Bean定义加载器将启动类在内的资源加载到Bean定义池
BeanDefinitionMap
中,以便后续根据Bean定义创建Bean对象。 - 发布资源加载完成事件。
填充容器
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.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
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.
onRefresh();
// Check for listener beans and register them.
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...
resetCommonCaches();
contextRefresh.end();
}
}
}
-
通过
prepareRefresh
方法,在已有的系统环境基础上准备servlet相关的环境Environment,通过initServletPropertySources
对servlet初始化参数servletContextInitParams
跟servletConfigInitParams
进行赋值。然后通过validateRequiredProperties
方法检验是否有必填的环境变量。 -
通过
obtainFreshBeanFactory
,通过obtainFreshBeanFactory
重新构造BeanFactory。 -
prepareBeanFactory
方法主要在BeanFactory中准备BeanClassLoader(类加载器)
、BeanExpressionResolver(表达式解析器)
、PropertyEditorRegistrar(配置文件处理器)
等系统级处理器,用以解析Aware
接口的ApplicationContextAwareProcessor
,用来处理自定义监听器注册和注销的ApplicationListenerDetector
。同时会注册一些特殊的Bean和系统级Bean,比如容器本身BeanFactory
和ApplicationContext
,系统环境environment
,系统属性systemProperties
将它们放入特殊对象池和单例池中。 -
通过
postProcessBeanFactory
对BeanFactory进行额外设置或修改,这里主要定义了包括request
、session
在内的servlet相关作用域Scopes,同时也注册跟Servlet相关的一些特殊Bean。包括ServletRequest,ServletResponse,HttpSession,WebRequest
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, @Nullable ServletContext sc) { beanFactory.registerScope("request", new RequestScope()); beanFactory.registerScope("session", new SessionScope()); if (sc != null) { ServletContextScope appScope = new ServletContextScope(sc); beanFactory.registerScope("application", appScope); sc.setAttribute(ServletContextScope.class.getName(), appScope); } beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); }
-
执行
invokeBeanFactoryPostProcessors
,会逐一执行在第三个大阶段容器创建中注册的各种BeanFactory后置处理器beanFactoryPostProcessor
。其中最主要的就是用来加载所有Bean定义的ConfigurationClassPostProcessor
,通过它加载所有的@Configuration
配置类。同时检索指定的Bean扫描路径compoentScans
,然后通过Bean扫描器ClassPathBeanDefinitionScanner
中doScan
扫描每个类,将所有扫描的Bean定义,都放到Bean定义池beanDefinitionMap
中。同样也会扫描所有加了@Bean、@Import
等注解的类和方法,将它们对应的Bean定义也都放到Bean定义池中。 -
通过
registerBeanPostProcessors
检索所有的Bean后置处理器,同时根据指定的order进行排序,然后放入后置处理器池beanPostProcessors
中,没一个后置处理器都会在Bean初始化之前和之后分别执行对应的逻辑。public static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); for (String ppName : postProcessorNames) { postProcessors.add(beanFactory.getBean(ppName,BeanPostProcessor.class)) } sortPostProcessors(postprocessors, beanFactory); registerBeanPostProcessors(beanFactory, postprocessors); // ... }
-
initMessageSource
从单例池中获名为messageSource
的Bean放入ApplicationContext
中用于实现国际化。 -
initApplicationEventMulticaster
从单例池中获取名为applicationEventMulticaster
的Bean放入ApplicationContext
中用于自定义广播事件,有了它就可以通过publishEvent
方法进行事件的发布。 -
通过
onRefresh
构造并启动Web服务器,先查找实现了ServletWebServerFactory
这个接口的应用服务器Bean,默认是tomcat,接下来通过getWebServer
方法构造一个Tomcat对象,同时通过start方法启动。这样容器内的web服务器就开始运行了 -
registerListeners
,在bean中查找所有的监听器Bean,将它们注册到第8步构造的消息广播器applicationEventMulticaster
中 -
finishBeanFactoryInitialization
这一步来生产我们所有的Bean整体分为:构造对象、填充属性、初始化实例、注册销毁 四个步骤 -
finishRefresh
构造并注册lifecycleProcessor(生命周期管理器)
,同时会调用所有实现了LifeCycle
的Bean中的start方法。发布一个容器刷新完成的事件。
执行Runner
Spring Boot提供了ApplicationRunner
和CommandLineRunner
两种类型的接口,可以让我们在容器启动完成后,来回调执行run
方法。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
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);
}
}
}