本文作为SpringBoot系列的开篇,介绍SpringBoot的启动流程,包括Spring容器和Tomcat启动过程。SpringBoot作为流行的微服务框架,其是基于约定和自动装配机制对Spring的封装和增强。
由于前面的Spring系列对Spring容器已经进行了较为细致的梳理,相同内容不进行重复说明。
添加SpringBoot和web依赖:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.2.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
启动类:
@SpringBootApplication
// 标注[1]
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Note:
按规范,一般将main
方法所在的类命名为artifactId+Application.
跟踪SpringApplication.run(DemoApplication.class, args)
:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
从逻辑上可以拆分为两个部分:构造SpringApplication对象,调用SpringApplication对象的run方法.
new SpringApplication(primarySources), primarySources参数为传入的DemoApplication.class对象;
说明:传入的primarySources不一定为main方法所在类,只需要保证为@SpringBootApplication注解的类即可。
经过public SpringApplication(Class>… primarySources) {this(null, primarySources);}进入:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// resourceLoader为null;
this.resourceLoader = resourceLoader;
// 主配置类,此时为DemoApplication.class
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// web类型为SERVLET
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 使用SPI机制加载ApplicationContextInitializer和ApplicationListener类型的对象,并保存到initializers和listeners属性中;[标注1]
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推算出main方法所在类(通过构造异常对象获取), 此时为DemoApplication.class
this.mainApplicationClass = deduceMainApplicationClass();
}
Note:
getSpringFactoriesInstances
方法逻辑如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 从spring.factories文件中加载指定名称的类型,此时为ApplicationContextInitializer和ApplicationListener
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通过反射创建对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序(继承了Ordered接口的使用getOrder获取,使用@Order注解的根据注解的值)
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
ApplicationContextInitializer包括:
// spring-boot包中的spring.factories文件:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
// spring-boot-autoconfiguration包中的spring.factories文件:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
ApplicationListener包括:
// spring-boot包中的spring.factories文件:
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
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.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
// spring-boot-autoconfiguration包中的spring.factories文件:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
删除计时、Banner打印、日志打印、异常逻辑后:
public ConfigurableApplicationContext run(String... args) {
// 对java.awt.headless参数设置[Ignore]
this.configureHeadlessProperty();
// 使用SPI机制获取SpringApplicationRunListeners,
// 内部仅包含一个EventPublishingRunListener类型的监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
// 对启动方法的入参进行包装
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建环境变量 [标注1]
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// spring.beaninfo.ignore属性设置[Ignore]
this.configureIgnoreBeanInfo(environment);
// 根据Servlet类型创建Spring容器 [标注2]
ConfigurableApplicationContext context = this.createApplicationContext();
// 刷新前的预处理 [标注3]
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器 标注[4]
this.refreshContext(context);
// 容器刷新后处理,预留扩展接口,此时逻辑为空
this.afterRefresh(context, applicationArguments);
listeners.started(context);
// 调用run方法 [标注5]
this.callRunners(context, applicationArguments);
// 返回AnnotationConfigServletWebServerApplicationContext类型的Spring容器
return context;
}
prepareEnvironment
方法用户构造环境变量,类型为StandardServletEnvironment,包括:
[1] systemProperties保存系统属性,如java.version, user.name;
[2] systemEnvironment保存环境变量,如JAVA_HOME, GRADLE_HOME;
[3] applicationConfig保存application.yml配置文件信息 …
protected ConfigurableApplicationContext createApplicationContext() {
// 省略部分代码 ...
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
webApplicationType
为SERVLET类型,通过反射构造一个AnnotationConfigServletWebServerApplicationContext类型的对象返回。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 为Spring容器设置环境变量
context.setEnvironment(environment);
// 对Spring容器的beanNameGenerator(默认启动流程为空)、resourceLoader(默认启动流程为空)、addConversionService组件进行设置
postProcessApplicationContext(context);
// 调用initializers的initialize方法,在new SpringApplication阶段通过SPI获取的ApplicationContextInitializer对象列表
applyInitializers(context);
listeners.contextPrepared(context);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 将启动入参和printedBanner注册到IOC中
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// SpringBoot不支持循环依赖
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// lazyInitialization为false, 不进行懒加载(LazyInitializationBeanFactoryPostProcessor会将容器中的BeanDefinition的lazyInit属性设置为true,从而实现懒加载)
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 向容器导入主配置类,此时为DemoApplication[标注3-1]
Set<Object> sources = getAllSources();
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
跟踪load
方法进入:
class BeanDefinitionLoader {
// ...
private int load(Class<?> source) {
// ...
this.annotatedReader.register(source);
}
}
public class AnnotatedBeanDefinitionReader {
// ...
public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass, null, null, null, null);
}
}
doRegisterBean
方法表示向IOC容器中注册一个beanClass类型的对象,此时为DemoApplication. registerBean
方法实现逻辑的解读是Spring导入Bean对象的内容,本文不进行深究。
this.refreshContext(context);
内容如下
private void refreshContext(ConfigurableApplicationContext context) {
this.refresh((ApplicationContext)context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
逻辑可以分为两个部分:
调用this.refresh((ApplicationContext)context);
刷新容器,调用context.registerShutdownHook()
注册关闭时的勾子函数。context.registerShutdownHook()是通过Runtime.getRuntime().addShutdownHook
方法向JVM注册钩子函数,在当JVM关闭时执行。
这里需要执行的逻辑包括:注销Bean、关闭Bean工厂、清理缓存等。
进入refresh
方法:
public void refresh() throws BeansException, IllegalStateException {
//... 省略部分与主体逻辑无关的代码
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
}
上述流程在 Spring系列-1 启动流程 中以及进行介绍过, 重复内容这里不进行说明。
此处刷新的Spring容器类型为AnnotationConfigServletWebServerApplicationContext,重写了onRefresh
方法:
protected void onRefresh() {
// 空方法
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
即onRefresh()的核心逻辑在this.createWebServer()
方法中,该方法用于创建并启动web容器,逻辑如下:
private void createWebServer() {
// this.webServer为null
WebServer webServer = this.webServer;
// servletContext为null
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//获取web容器工厂 [标注4-1]
ServletWebServerFactory factory = getWebServerFactory();
// 构造web容器 [标注4-2]
this.webServer = factory.getWebServer(getSelfInitializer());
// 向IOC注册webServerGracefulShutdown和webServerStartStop对象 [标注4-3]
getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
// Ignore...
}
//占位符替换:替换环境变量中名称"servletContextInitParams"的资源->容器对象
initPropertySources();
}
Note 4-1:getWebServerFactory()
用于获取容器工厂
spring-boot-autoconfigure包中定义了ServletWebServerFactoryConfiguration配置类,定义了Tomcat、Jetty、Undertow容器工厂配置类:
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(...) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
//...
return factory;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedJetty {
@Bean
JettyServletWebServerFactory JettyServletWebServerFactory(...) {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
//...
return factory;
}
}
//...
}
其中EmbeddedTomcat的注入条件是IOC容器中还没有注入ServletWebServerFactory对象,以及类路径中存在Tomcat.class
类;而Tomcat.class
以及Tomcat相关依赖定义在spring-boot-starter-tomcat包中,在引入spring-boot-starter-web依赖时,会自动引入spring-boot-starter-tomcat依赖,即springboot默认使用Tomcat容器。
SpringBoot也可以使用Jetty容器启动,pom依赖需要进行调整,删除spring-boot-starter-web中引入的tomcat依赖,并引入jetty依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
<version>2.6.4version>
dependency>
本文后续以SpringBoot默认的Tomcat为web容器进行介绍。
Note 4-2: this.webServer = factory.getWebServer(getSelfInitializer())
构造web容器
该步骤包含的内容较多,可以拆成两个部分:
(1) 参数部分:是一个lambda表达式,将被作为参数传入到Tomcat类中,在Tomcat启动时被调用;
(2) 函数部分:负责构造并启动web容器。
参数部分:
private void selfInitialize(ServletContext servletContext) throws ServletException {
// prepareWebApplicationContext用于实现web容器上下文(ApplicationContextFacade类型)与Spring容器相互指向
prepareWebApplicationContext(servletContext);
// 将servletContext包装成ServletContextScope对象,作为Scope对象添加到容器的中以及设置到servletContext的application属性
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
WebApplicationContextUtils.registerEnvironmentBeans
方法将servletContext以"servletContext"
为BeanName注册到IOC中,并从servletContext中获取InitParameter和Attribute信息分别以"contextParameters"
和""为beanName注册到IOC中。 getServletContextInitializerBeans()
方法从IOC中获取ServletContextInitializer、Filter、Servlet对象, 封装为RegistrationBean对象,并调用这些对象的onStartup
方法。以DispatcherServletRegistrationBean为例进行介绍:
public final void onStartup(ServletContext servletContext) throws ServletException {
// "servlet dispatcherServlet"
String description = getDescription();
register(description, servletContext);
}
register(description, servletContext)方法包含两个主要步骤addRegistration和configure:
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
// "dispatcherServlet"
String name = getServletName();
// this.servlet就是DispatcherServlet对象,将其注册到serverContext容器上下文中
return servletContext.addServlet(name, this.servlet);
}
protected void configure(ServletRegistration.Dynamic registration) {
super.configure(registration);
String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
if (urlMapping.length == 0 && this.alwaysMapUrl) {
urlMapping = DEFAULT_MAPPINGS;
}
// 注册urlMapping, 默认是 "/"
if (!ObjectUtils.isEmpty(urlMapping)) {
registration.addMapping(urlMapping);
}
// 设置loadOnStartup, 默认是-1,表示懒加载
registration.setLoadOnStartup(this.loadOnStartup);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
}
继续进入factory.getWebServer(getSelfInitializer())
方法:
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {Registry.disableRegistry();} // Ignore
// 创建Tomcat对象,并设置service、connector、engine、Host
// 这个Tomcat来自org.apache.catalina.startup包
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
// 给Host添加一个Context, 该Context被封装后就是前文提到的web容器上下文
// initializers属性被保存在Context中, 在tonmcat启动后调用initializers的onStartUp方法(上文涉及的lambda表达式).
prepareContext(tomcat.getHost(), initializers);
// 构造、启动并返回web容器[标注3]
return getTomcatWebServer(tomcat);
}
getTomcatWebServer(tomcat)
方法逻辑如下:
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
new TomcatWebServer(tomcat, getPort() >= 0, Shutdown.IMMEDIATE);
}
其中:getPort()
得到的端口来源为Spring配置文件, 因此getPort()>0
; Shutdown.IMMEDIATE
为枚举变量。
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
this.tomcat = tomcat;
// true
this.autoStart = autoStart;
// 传入的Shutdown.IMMEDIATE标志立即关闭(而非优雅关闭),因此this.gracefulShutdown设置为null
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
initialize();
}
initialize
方法中只有两处逻辑需要注意:
private void initialize() throws WebServerException {
// ...
// 启动Tomcat
this.tomcat.start();
// ...
// 开启一个非守护线程,因为Tomcat所有线程为守护线程,否则会直接退出进程
startDaemonAwaitThread();
}
Note 4-3:
// 向IOC注册webServerGracefulShutdown和webServerStartStop对象 [标注3]
getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));
将得到的webServer对象进行包装并注册到IOC中。其中: 可通过WebServerGracefulShutdownLifecycle对象优雅地关闭容器,WebServerStartStopLifecycle可以进行容器的启停。优雅关闭指等待所有正在处理的请求完成后再关闭,以确保所有连接都被正确地关闭。
this.callRunners(context, applicationArguments);
逻辑如下:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
// 从IOC容器获取所有的ApplicationRunner类型的Bean对象
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 从IOC容器获取所有的CommandLineRunner类型的Bean对象
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 按照Ordered接口和@Order注解进行排序
AnnotationAwareOrderComparator.sort(runners);
// 按顺序调用ApplicationRunner或CommandLineRunner的run方法 [标注5-1]
for (Object runner : runners) {
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}
}
Note:5-1
调用ApplicationRunner和CommandLineRunner时,有try-catch异常保护,不会因为某个runner执行异常而影响其他runner执行,传入runner的参数就是容器启动时传给SpringApplication的参数,即main
方法的参数。