本文基于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
,这里不再详细讲解。
流程图
流程图如下: