本文基於springboot 2.2.x分支源碼。
相關的自動配置類
關於Servlet、web內容在org.springframework.boot.autoconfigure.web.servlet包下面:
該包下面主要有四個自動配置類:DispatcherServletAutoConfiguration、HttpEncodingAutoConfiguration、ServletWebServerFactoryAutoConfiguration、WebMvcAutoConfiguration
- DispatcherServletAutoConfiguration:該類主要做DispatcherServlet相關的自動配置
- HttpEncodingAutoConfiguration:該類主要做http字符編碼相關的配置
- ServletWebServerFactoryAutoConfiguration:該類做的是Servlet web容器工廠相關配置
- WebMvcAutoConfiguration:該類做的是springvc相關的配置,例如配置一些消息轉換器、視圖解析器、包括HandlerAdapter、HandlerMapping等相關配置
因為我們要了解springboot啟動內置tomcat以及加載DispatcherServlet的原理,就需要深入DispatcherServletAutoConfiguration和ServletWebServerFactoryAutoConfiguration類了。
ServletWebServerFactoryAutoConfiguration
先看該類的注解:
@Configuration(proxyBeanMethods = false) //配置類
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//自動配置順序
@ConditionalOnClass(ServletRequest.class)//類路徑有ServletRequest類
@ConditionalOnWebApplication(type = Type.SERVLET)//SERVLET的web環境
@EnableConfigurationProperties(ServerProperties.class)//使ServerProperties生效
//導入四個組件
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
//導入ServletWebServerFactoryCustomizer組件,主要對ServletWebServerFactory做定制化配置
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
//導入TomcatServletWebServerFactoryCustomizer組件,主要對TomcatServletWebServerFactory做定制化配置
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
//其他方法省略
}
先看ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar:
該類實現了ImportBeanDefinitionRegistrar,所以在ConfigurationClassPostProcessor解析配置類的時候,自動調用該接口的registerBeanDefinitions方法,
這里的BeanPostProcessorsRegistrar在該方法中注冊了兩個bean的定義信息:一個是ErrorPageRegistrarBeanPostProcessor,這個暫且不用管,主要是給ErrorPageRegistry中注冊錯誤頁的。
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
//留下主要方法,其他方法省略
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
另一個是注冊的bean定義信息是WebServerFactoryCustomizerBeanPostProcessor,這個類就比較有用了,看我在代碼中的注釋。
//實現了BeanPostProcessor,會攔截指定bean的創建
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List<WebServerFactoryCustomizer<?>> customizers;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//當創建的bean是WebServerFactory時,會調用postProcessBeforeInitialization(WebServerFactory) bean)
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
@SuppressWarnings("unchecked")
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
//getCustomizers():拿到容器中所有注冊的WebServerFactoryCustomizer組件
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
//依次執行WebServerFactoryCustomize的customize方法,該方法主要是對WebServerFactory工廠做一些定制化的工作(配置)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
//其他方法省略。。。
}
再回來看ServletWebServerFactoryAutoConfiguration,還導入了其他三個組件。
-
ServletWebServerFactoryConfiguration.EmbeddedTomcat
-
ServletWebServerFactoryConfiguration.EmbeddedJetty
-
ServletWebServerFactoryConfiguration.EmbeddedUndertow
默認情況,只有EmbeddedTomcat會生效,看它的代碼:導入了一個TomcatServletWebServerFactory類型的組件,傳入三個ObjectProvider類型參數,都是對Connector、Context、ProtocolHandler做定制化操作的,默認情況下這三個參數都沒有配置,如果想要配置,只需要自己在容器中導入TomcatConnectorCustomizer、TomcatContextCustomizer、TomcatProtocolHandlerCustomizer組件就行了。
記得這里導入了TomcatServletWebServerFactory,后面tomcat的創建就靠它。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
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;
}
}
DispatcherServletAutoConfiguration
再看這個配置類,這里面導入了兩個比較重要的組件:
1.導入DispatcherServlet這個組件,注意這個類僅僅只是保存到IOC容器中,並沒有在tomcat中初始化
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//自動配置順序
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)//在ServletWebServerFactoryAutoConfiguration自動配置之后再自動配置
public class DispatcherServletAutoConfiguration {
/**
* The bean name for a DispatcherServlet that will be mapped to the root URL "/".
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/**
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
//
}
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
}
2.導入了DispatcherServletRegistrationBean組件,先看該類的繼承樹,看看它繼承了哪些關鍵組件。

可以觀察到,該類實現了ServletContextInitializer接口,再看接口上的英文注釋。
Interface used to configure a Servlet 3.0+ {@link ServletContext context}
programmatically. Unlike {@link WebApplicationInitializer}, classes that implement this
interface (and do not implement {@link WebApplicationInitializer}) will not be
detected by {@link SpringServletContainerInitializer} and hence will not be
automatically bootstrapped by the Servlet container.
This interface is designed to act in a similar way to
{@link ServletContainerInitializer}, but have a lifecycle that's managed by Spring and
not the Servlet container.
For configuration examples see {@link WebApplicationInitializer}.
大致含義:這個接口不像WebApplicationInitializer一樣被SpringServletContainerInitializer自動探測,因此不會自動被servlet容器啟動。但是它仍有着被spring管理的生命周期。
斷點調試
onRefresh
springboot啟動tomcat時候,日志會打印tomcat啟動的日志。我們只要在springboot 的run方法上打上斷點,不難找到tomcat啟動在哪里。
在SpringApplication類的run方法中,這里調用了refreshContext,這一步是刷新ioc容器,這里斷點一過,tomcat啟動日志就打印了,所以tomcat就在這里面。

走到經典的容器刷新方法,斷點一走過onRefresh方法,tomcat啟動日志就打印了,所以這里就是tomcat啟動的地方。
createWebServer
進入onRefresh方法,先調用父類的onRefresh方法,然后調用createWebServer方法:
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
再看createWebServer方法:
private void createWebServer() {
//默認情況下,this.webServer和servletContext都為null
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//獲取WebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
//從WebServerFactory中獲取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();
}
那么重點就是ServletWebServerFactory factory = getWebServerFactory();和this.webServer = factory.getWebServer(getSelfInitializer());。
getWebServerFactory()
先看這個getWebServerFactory()方法:
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
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);
}
第一步:從容器中獲取ServletWebServerFactory組件名,默認情況下,只有在ServletWebServerFactoryAutoConfiguration自動配置的TomcatServletWebServerFactory。
第二步:校驗組件名,只有一個組件名的時候,不會拋出異常,如果找不到組件或者組件有多個都會拋出ApplicationContextException。
第三步:從容器中獲取到ServletWebServerFactory組件,默認就是TomcatServletWebServerFactory。
getSelfInitializer()
先看getSelfInitializer(),這個方法比較重要:內部是一個selfInitialize回調方法,並不會立馬執行,這里先提一嘴,后面會說到。
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
//同時這個selfInitialize方法不會立刻調用,只是作為一個回調方法,當ServletContextInitializer.onStartUp調用時,才會被調用
private void selfInitialize(ServletContext servletContext) throws ServletException {
//准備WebApplicationContext,主要做一些校驗和設置屬性
prepareWebApplicationContext(servletContext);
//設置ApplicationScope
registerApplicationScope(servletContext);
//注冊一些關於Servlet環境的組件
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//getServletContextInitializerBeans(),主要是創建了一個ServletContextInitializerBeans集合
//對ServletContextInitializerBeans集合遍歷,是對它內部的sortedList進行遍歷,里面存放了容器中的所有ServletContextInitializer
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
//調用所有的ServletContextInitializer的onStartup方法
beans.onStartup(servletContext);
}
}
對於這種寫法,可能看起來有點費解,因為getSelfInitializer()的返回值是一個ServletContextInitializer,同時它也是一個函數式接口,所以等價於下面這種寫法:
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return servletContext -> {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
};
}
getWebServer
再看getWebServer方法:
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//new了一個tomcat
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
//設置baseDir
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
//添加一個connector
tomcat.getService().addConnector(connector);
//自定義connector
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
//配置引擎
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
//准備Context
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
看prepareContext方法:
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
//創建TomcatEmbeddedContext
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);
try {
context.setCreateUploadTargets(true);
}
catch (NoSuchMethodError ex) {
// Tomcat is < 8.5.39. Continue.
}
configureTldPatterns(context);
//設置WebappLoader
WebappLoader loader = new WebappLoader();
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
}
//增加LifecycleListener
context.addLifecycleListener(new StaticResourceConfigurer(context));
//合並Initializers,主要是添加一些其他的ServletContextInitializer
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
//配置context
configureContext(context, initializersToUse);
postProcessContext(context);
}
進入configureContext方法:
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
//創建了TomcatStarter,這個實現了ServletContainerInitializer接口
//對servlet3.0規范熟悉的知道,tomcat在servlet容器初始化的時候,會調用所有的ServletContainerInitializer接口的onStartup方法
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
//添加到context中
context.addServletContainerInitializer(starter, NO_CLASSES);
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
//添加tomcat閥門
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);
//對TomcatContext進行自定義配置
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
}
到這里,看了一大半,有人可能一臉蒙蔽,我是誰,我在哪兒?這里究竟做了哪些關鍵步驟?
我來解釋一下在getWebServer方法截至運行到prepareContext,springboot究竟做了哪些事情。
- getSelfInitializer返回的是一個函數式接口,內部主要是拿到springboot中的所有
ServletContextInitializer,並依次執行它的onStartup方法 - 把這個函數式接口作為參數傳入到getWebServer方法中
- getWebServer方法主要做了以下幾件事
- 創建了一個Tomcat實例,設置了一些屬性
- 為tomcat的Host配置Context
- 創建了一個
TomcatStarter並把上面的getSelfInitializer返回的函數式接口設置到它的成員變量中,TomcatStarter實際上是ServletContainerInitializer,tomcat在servlet容器初始化的時候,會調用所有的ServletContainerInitializer接口的onStartup方法 - 然后把
TomcatStarter添加到context中
斷點繼續往下走,走到getTomcatWebServer(tomcat),進去:直接new了一個TomcatWebServer,並把前面創建的tomcat傳進去。
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
斷點繼續往里面走:設置了屬性后,直接調用initialize方法:
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
initialize方法如下:終於在這一步,看到了tomcat.strart()方法,至此,tomcat終於啟動。
private void initialize() throws WebServerException {
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
removeServiceConnectors();
}
});
//tomcat啟動
this.tomcat.start();
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
}
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
TomcatStarter.onStartup
tomcat.start()方法啟動后,因為TomcatStarter實現了ServletContainerInitializer接口,在前面手動調用了context.addServletContainerInitializer(starter, NO_CLASSES);,把tomcatStart添加到context中,所以這里的TomcatStarter並不是通過SPI機制加載到tomcat中,這里與springmvc處理不一樣。
tomcat啟動后,會執行ServletContainerInitializer的onStartUp方法,所以TomcatStarter會被調用:
@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());
}
}
}
這里的this.initializers有三個,我們只要管前面傳進來的getSelfInitializer()方法:這里前面提到的selfInitialize方法才會被調用
private void selfInitialize(ServletContext servletContext) throws ServletException {
//准備WebApplicationContext,主要做一些校驗和設置屬性
prepareWebApplicationContext(servletContext);
//設置ApplicationScope
registerApplicationScope(servletContext);
//注冊一些關於Servlet環境的組件
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//getServletContextInitializerBeans(),主要是創建了一個ServletContextInitializerBeans集合
//對ServletContextInitializerBeans集合遍歷,是對它內部的sortedList進行遍歷,里面存放了容器中的所有ServletContextInitializer
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
//調用所有的ServletContextInitializer的onStartup方法
beans.onStartup(servletContext);
}
}
這里getServletContextInitializerBeans()返回了四個ServletContextInitializer:

其中第一個,就是配置類中配置的DispatcherServletRegistrationBean,而springboot通過該類注冊了DispatcherServlet,我們接下來來看springboot是怎么注冊的。
注冊DispatcherServlet
進入DispatcherServletRegistrationBean的onstart方法,直接進到它的父類RegistrationBean:
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
//調用register
register(description, servletContext);
}
斷點進入register方法
@Override
protected final void register(String description, ServletContext servletContext) {
//執行addRegistration
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
//調用configure
configure(registration);
}
進入addRegistration,終於走進DispatcherServletRegistrationBean類中:直接調用servlet原生方法
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
//添加servlet
return servletContext.addServlet(name, this.servlet);
}
這里this.servlet也就是DispatcherServlet,是在前面DispatcherServletAutoConfiguration 自動配置類中傳入到DispatcherServletRegistrationBean類中的。
方法出來后,走進configure方法:這里會給ServletRegistration.Dynamic設置一些屬性,這也是javax.servlet的原生方法。
@Override
protected void configure(ServletRegistration.Dynamic registration) {
super.configure(registration);
String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
if (urlMapping.length == 0 && this.alwaysMapUrl) {
urlMapping = DEFAULT_MAPPINGS;
}
if (!ObjectUtils.isEmpty(urlMapping)) {
registration.addMapping(urlMapping);
}
registration.setLoadOnStartup(this.loadOnStartup);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
}
至此,tomcat啟動了,DispatcherServlet注冊到tomcat中,同時RegistrationBean也會注冊javax原生的監聽器和過濾器,具體實現是ServletListenerRegistrationBean和FilterRegistrationBean,這里不再詳細講解。
流程圖
流程圖如下:

