SpringBoot内嵌Servlet容器启动过程
我们在启动springboot应用的时候基本都是从以下的代码开始
public static void main( String[] args ) {
SpringApplication.run(Application.class, args);
}
下面是SpringApplication中定义的run方法:
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
//调用下面这个静态run方法
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//创建实例并执行run方法
return new SpringApplication(primarySources).run(args);
}
... ...
}
可见其实例化了一个SpringApplication实例,同时执行其run方法。其执行的构造方法如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//就是SpringApplication.run(Application.class, args)中的第一个参数
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//根据classpath中存在的类型进行判断是reactive还是普通的web应用程序
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从META-INF/spring.factories查找ApplicationContextInitializer类型的配置,
//然后通过反射将其实例化保存到initializers变量中
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推导出主要应用类并加载(也就是我们执行main方法的那个类)
this.mainApplicationClass = deduceMainApplicationClass();
}
接着便开始执行SpringApplication实例的run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//根据所在环境创建对应的应用上下文
//【AnnotationConfigServletWebServerApplicationContext】
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//重点:刷新上下文,通过调用context的refresh()方法,真正开始初始化Context容器,
//同时在onRefresh阶段便开始创建和启动servlet容器了,
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 {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
对于Servlet Web应用程序,其实例化的上下文AnnotationConfigServletWebServerApplicationContext在初始化的onRefresh阶段会通过以下的createWebServer方法来创建Servlet容器
一:创建WebServer
AnnotationConfigServletWebServerApplicationContextpublic class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//获取用于构建WebServer的工厂类
ServletWebServerFactory factory = getWebServerFactory();
//从BeanFactory中得到所有的ServletContextInitializer并作为参数来创建WebServer,
//等调用WebServer的onStartup方法时对ServletContext进行装配。
//通过这种方法就能完成拦截器、过滤器的装配工作。
//在finishRefresh阶段将会启动WebServer(见下面的方法)
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();
}
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
//从BeanFactory中得到ServletWebServerFactory类型的Bean
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
//返回ServletContextInitializer的实现类,其onStartup方法的实现为先的selfInitialize方法
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
//Tomcat启动过程中的回调方法
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
//启动WebServer
@Override
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
private WebServer startWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.start();
}
return webServer;
}
}
ServletContextInitializer
从上面来看,从BeanFactory中得到ServletWebServerFactory类型的Bean是关键,那么什么时候在哪里完成了ServletWebServerFactory类型实例的注册工作呢?我们从SpringBoot的自动化配置中来寻找。
二:创建ServletWebServerFactory类型Bean
首先看下TomcatServletWebServerFactory的继承关系
TomcatServletWebServerFactory
在spring-boot-autoconfigure-2.2.1/META-INF/spring.factories中有以下配置:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration
ServletWebServerFactoryAutoConfiguration
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
//收集通过“server.”开始配置的参数
@EnableConfigurationProperties(ServerProperties.class)
//导入了三种服务器Tomcat、Jetty和Undertow的配置
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
//用于给ConfigurableServletWebServerFactory进行配置
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
//用于给TomcatServletWebServerFactory进行参数的定制
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
... ...
}
这里有个ServerProperties对象值得我们重点关注,里面有个ErrorProperties属性定义了错误处理。同时可进一步阅读
org.springframework.boot.web.servlet.support.ErrorPageFilterConfiguration、
org.springframework.boot.web.servlet.support.ErrorPageFilter、
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration类中对请求过程中的错误处理
ServletWebServerFactoryConfiguration
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
//Tomcat的配置
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
}
在这里便生成了ServletWebServerFactory类型的TomcatServletWebServerFactory 实例。
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
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
prepareContext(tomcat.getHost(), initializers);
//创建并返回Server
return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new LoaderHidingResourceRoot(context));
}
context.setName(getContextPath());
context.setDisplayName(getDisplayName());
//设置应用上下文路径
context.setPath(getContextPath());
//设置应用的根路径
File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader());
resetDefaultLocaleMapping(context);
addLocaleMappings(context);
context.setUseRelativeRedirects(false);
try {
context.setCreateUploadTargets(true);
}
catch (NoSuchMethodError ex) {
// Tomcat is < 8.5.39. Continue.
}
configureTldSkipPatterns(context);
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
}
context.addLifecycleListener(new StaticResourceConfigurer(context));
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
//在Host下面添加应用的Context
host.addChild(context);
//装配Tomcat的上下文
configureContext(context, initializersToUse);
postProcessContext(context);
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
//继续下面的TomcatStarter
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
context.addServletContainerInitializer(starter, NO_CLASSES);
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
for (Valve valve : this.contextValves) {
context.getPipeline().addValve(valve);
}
for (ErrorPage errorPage : getErrorPages()) {
org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
tomcatErrorPage.setLocation(errorPage.getPath());
tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
context.addErrorPage(tomcatErrorPage);
}
for (MimeMappings.Mapping mapping : getMimeMappings()) {
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
}
configureSession(context);
new DisableReferenceClearingContextCustomizer().customize(context);
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
}
}
应用ServletContextInitializer
class TomcatStarter implements ServletContainerInitializer {
private static final Log logger = LogFactory.getLog(TomcatStarter.class);
private final ServletContextInitializer[] initializers;
private volatile Exception startUpException;
TomcatStarter(ServletContextInitializer[] initializers) {
this.initializers = initializers;
}
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
this.startUpException = ex;
// Prevent Tomcat from logging and re-throwing when we know we can
// deal with it in the main thread, but log for information here.
if (logger.isErrorEnabled()) {
logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
+ ex.getMessage());
}
}
}
}
SpringBoot应用程序启动过程小结
1,执行SpringApplication的静态run方法
创建SpringApplication的实例,执行无参构造方法,主要做了一下几个事:
- 根据classpath中存在的类型进行判断是reactive还是普通的web应用程序
- 从META-INF/spring.factories查找ApplicationContextInitializer、ApplicationListener类型的配置,然后通过反射将其实例化保存到变量中
- 推导出主要应用启动类并加载(也就是我们执行main方法的那个类)
2,执行SpringApplication实例的非静态run方法
1)根据应用程序类型创建应用上下文
-
servlet 应用程序:
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext -
Reactive应用程序:
org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext -
普通java应用程序:
org.springframework.context.annotation.AnnotationConfigApplicationContext
2)执行SpringApplication的refresh()方法
该方法会执行context的refresh()方法,然后开始真正构建BeanFactory
-
执行onRefresh()
通过该阶段来创建一些特殊的Bean,这里便是在创建Servlet容器。
a)从BeanFactory中得到ServletWebServerFactory类型的Bean
b)调用ServletWebServerFactory的getWebServer(getSelfInitializer())方法来完成TomcatServer的创建(getSelfInitializer()方法得到的就是ServletContextInitializer类型用于回调的对象)【回调目的是等Tomcat创建好ServletContext对象后可对其进行装配】
c) 当Spring框架执行到finishRefresh阶段时便开始启动Tomcat,随着回调方法被执行
启动Servlet容器