spring boot启动加载tomcat原理深度剖析
Posted ac_dao_di
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring boot启动加载tomcat原理深度剖析相关的知识,希望对你有一定的参考价值。
1. 前言
spring boot打包成fat jar的形式启动时,这时tomcat作为内嵌容器,由spring boot带动起来,并注册servlet/filter等,这个过程是如何实现的呢?本文将从源码级别带你探索spring boot带起tomcat的实现原理。
以spring boot demo这个模块为例,一步一步深入挖掘,着重看内嵌tomcat如何启动,并讲下外部tomcat启动spring boot的原理。
2. 在bean definition加载完毕后,启动并初始化tomcat
一般情况下,我们是使用的是servlet,这时spring boot使用的是AnnotationConfigServletWebServerApplicationContext
,继承自ServletWebServerApplicationContext
,这个父类,对AbstractApplicationContext
的onRefresh方法进行了覆盖,在这个阶段加载了tomcat容器。
@Override
protected void onRefresh()
super.onRefresh();
try
createWebServer();
catch (Throwable ex)
throw new ApplicationContextException("Unable to start web server", ex);
那么,选择在这个扩展点创建的好处是什么呢?在onRefresh之前,已经使用BeanFactoryPostProcessor加载了所有的BeanDefinition,这个时候ApplicationContext可以getBean,获取到ServletContext的初始化器,例如servlet/filter等spring bean初始化器,对tomcat进行配置。
如下为创建tomcat的代码,可以发现代码有两个分支,刚好分为内外tomcat两种情况,内部tomcat的话,会进行创建并初始化;而外部tomcat仅仅会进行初始化,配置serlvet/filter等。
3. 外部tomcat加载SpringApplication
以war包的形式打包时,这个时候需要单独部署外部tomcat,由tomcat带动spring boot容器。
从代码可以发现,外部tomcat的话,servletContext必然是赋值的,这里是在哪赋值呢?外部tomcat留了什么钩子,可以让spring来编程初始化servelt/filter呢?
对于外部tomcat部署spring boot应用,我们会让main类继承SpringBootServletInitializer
,这是一个WebApplicationInitializer
,是spring对于servlet的扩展点。在tomcat启动时,会调用其onStartup方法,创建SpringApplication
,设置初始化器,最终会设置servletContext,完成整个SpringApplication的构建。
public abstract class SpringBootServletInitializer implements WebApplicationInitializer
protected Log logger; // Don't initialize early
private boolean registerErrorPageFilter = true;
/**
* Set if the @link ErrorPageFilter should be registered. Set to @code false if
* error page mappings should be handled via the server and not Spring Boot.
* @param registerErrorPageFilter if the @link ErrorPageFilter should be registered.
*/
protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter)
this.registerErrorPageFilter = registerErrorPageFilter;
@Override
public void onStartup(ServletContext servletContext) throws ServletException
servletContext.setAttribute(LoggingApplicationListener.REGISTER_SHUTDOWN_HOOK_PROPERTY, false);
// Logger initialization is deferred in case an ordered
// LogServletContextInitializer is being used
this.logger = LogFactory.getLog(getClass());
WebApplicationContext rootApplicationContext = createRootApplicationContext(servletContext);
if (rootApplicationContext != null)
servletContext.addListener(new SpringBootContextLoaderListener(rootApplicationContext, servletContext));
else
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
+ "return an application context");
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext)
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null)
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
// 设置ApplicationContextInitializer,在ApplicationContext构建后,立即设置ServletContext
builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
builder.contextFactory((webApplicationType) -> new AnnotationConfigServletWebServerApplicationContext());
// 在这里配置source
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty()
&& MergedAnnotations.from(getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class))
application.addPrimarySources(Collections.singleton(getClass()));
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter)
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
application.setRegisterShutdownHook(false);
return run(application);
/**
* @link ApplicationContextInitializer for setting the servlet context.
*
* @author Dave Syer
* @author Phillip Webb
* @since 2.0.0
*/
public class ServletContextApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableWebApplicationContext>, Ordered
private int order = Ordered.HIGHEST_PRECEDENCE;
private final ServletContext servletContext;
private final boolean addApplicationContextAttribute;
/**
* Create a new @link ServletContextApplicationContextInitializer instance.
* @param servletContext the servlet that should be ultimately set.
*/
public ServletContextApplicationContextInitializer(ServletContext servletContext)
this(servletContext, false);
/**
* Create a new @link ServletContextApplicationContextInitializer instance.
* @param servletContext the servlet that should be ultimately set.
* @param addApplicationContextAttribute if the @link ApplicationContext should be
* stored as an attribute in the @link ServletContext
* @since 1.3.4
*/
public ServletContextApplicationContextInitializer(ServletContext servletContext,
boolean addApplicationContextAttribute)
this.servletContext = servletContext;
this.addApplicationContextAttribute = addApplicationContextAttribute;
public void setOrder(int order)
this.order = order;
@Override
public int getOrder()
return this.order;
@Override
public void initialize(ConfigurableWebApplicationContext applicationContext)
applicationContext.setServletContext(this.servletContext);
if (this.addApplicationContextAttribute)
this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
applicationContext);
那么SpringBootServletInitializer#onStartup这个方法,是在哪调用的呢?是SpringServletContainerInitializer
,通过注释可以看到,因为这个类上有@HandlesTypes注解,指定了类接口WebApplicationInitializer
,servlet 3.0容器会自动扫描类路径下的WebApplicationInitializer实现类,并把扫描到的实现类集合作为第一个参数传递给SpringServletContainerInitializer
。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer
/**
* Delegate the @code ServletContext to any @link WebApplicationInitializer
* implementations present on the application classpath.
* <p>Because this class declares @@code HandlesTypes(WebApplicationInitializer.class),
* Servlet 3.0+ containers will automatically scan the classpath for implementations
* of Spring's @code WebApplicationInitializer interface and provide the set of all
* such types to the @code webAppInitializerClasses parameter of this method.
* <p>If no @code WebApplicationInitializer implementations are found on the classpath,
* this method is effectively a no-op. An INFO-level log message will be issued notifying
* the user that the @code ServletContainerInitializer has indeed been invoked but that
* no @code WebApplicationInitializer implementations were found.
* <p>Assuming that one or more @code WebApplicationInitializer types are detected,
* they will be instantiated (and <em>sorted</em> if the @@link
* org.springframework.core.annotation.Order @Order annotation is present or
* the @link org.springframework.core.Ordered Ordered interface has been
* implemented). Then the @link WebApplicationInitializer#onStartup(ServletContext)
* method will be invoked on each instance, delegating the @code ServletContext such
* that each instance may register and configure servlets such as Spring's
* @code DispatcherServlet, listeners such as Spring's @code ContextLoaderListener,
* or any other Servlet API componentry such as filters.
* @param webAppInitializerClasses all implementations of
* @link WebApplicationInitializer found on the application classpath
* @param servletContext the servlet context to be initialized
* @see WebApplicationInitializer#onStartup(ServletContext)
* @see AnnotationAwareOrderComparator
*/
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException
List<WebApplicationInitializer> initializers = Collections.emptyList();
if (webAppInitializerClasses != null)
initializers = new ArrayList<>(webAppInitializerClasses.size());
for (Class<?> waiClass : webAppInitializerClasses)
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass))
try
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
catch (Throwable ex)
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
if (initializers.isEmpty())
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers)
// servlet会找到所有的WebApplicationInitializer SPI,然后调用,其中就包括SpringBootServletInitializer的实现类
initializer.onStartup(servletContext);
package javax.servlet;
import java.util.Set;
/**
* ServletContainerInitializers (SCIs) are registered via an entry in the
* file META-INF/services/javax.servlet.ServletContainerInitializer that must be
* included in the JAR file that contains the SCI implementation.
* <p>
* SCI processing is performed regardless of the setting of metadata-complete.
* SCI processing can be controlled per JAR file via fragment ordering. If
* absolute ordering is defined, then only the JARs included in the ordering
* will be processed for SCIs. To disable SCI processing completely, an empty
* absolute ordering may be defined.
* <p>
* SCIs register an interest in annotations (class, method or field) and/or
* types via the @link javax.servlet.annotation.HandlesTypes annotation which
* is added to the class.
*
* @since Servlet 3.0
*/
public interface ServletContainerInitializer
/**
* Receives notification during startup of a web application of the classes
* within the web application that matched the criteria defined via the
* @link javax.servlet.annotation.HandlesTypes annotation.
*
* @param c The (possibly null) set of classes that met the specified
* criteria
* @param ctx The ServletContext of the web application in which the
* classes were discovered
*
* @throws ServletException If an error occurs
*/
void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
对应的java spi如下,在spring web中实现了servlet标准的扩展点ServletContainerInitializer
,该扩展点从3.0开始,且是为了替换web.xml而生的。
一张图总结外部tomcat启动时,加载SpringApplication的整个过程:
4. fat jar启动内嵌tomcat
如下,会从spring容器查找ServletWebServerFactory,然后调用其getWebServer方法,并把ServletContextInitializer初始化器传递进去。
//org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
private void createWebServer()
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null)
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
// (3)注册Lifecycle,启动后注册到consul/nacos,以及优雅销毁
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
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
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);
在ServletWebServerFactoryConfiguration
这个配置类里,如果有tomcat这个jar,会自动注入tomcat factory,也就是TomcatServletWebServerFactory
。ServletWebServerFactoryConfiguration
类由ServletWebServerFactoryAutoConfiguration
这个自动配置类导入。
@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
// 注册tomcat ,可以自定义配置,这里会加载进来
@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;
/**
* Nested configuration if Jetty is being used.
*/
@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(
ObjectProvider<JettyServerCustomizer> serverCustomizers)
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
/**
* Nested configuration if Undertow is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass( Servlet.class, Undertow.class, SslClientAuthMode.class )
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedUndertow
@Bean
UndertowServletWebServerFactory undertowServletWebServerFactory(
ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
ObjectProvider<UndertowBuilderCustomizer> builderCustomizers)
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.getDeploymentInfoCustomizers()
.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
@Bean
UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
ServerProperties serverProperties)
return new UndertowServletWebServerFactoryCustomizer(serverProperties);
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import( ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class )
public class ServletWebServerFactoryAutoConfiguration
// 注册bean,可以自定义容器设置,例如端口号
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers)
return new ServletWebServerFactoryCustomizer(serverProperties,
webListenerRegistrars.orderedStream().collect(Collectors.toList()),
cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties)
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
static class ForwardedHeaderFilterConfiguration
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties)
return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());
@Bean
FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(
ObjectProvider<ForwardedHeaderFilterCustomizer> customizerProvider)
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
customizerProvider.ifAvailable((customizer) -> customizer.customize(filter));
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
interface ForwardedHeaderFilterCustomizer
void customize(ForwardedHeaderFilter filter);
/**
* Registers a @link WebServerFactoryCustomizerBeanPostProcessor. Registered via
* @link ImportBeanDefinitionRegistrar for early registration.
*/
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException
if (beanFactory instanceof ConfigurableListableBeanFactory)
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry)
if (this.beanFactory == null)
return;
// 注册WebServerFactoryCustomizerBeanPostProcessor,对webServerFactory进行初始化
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class,
WebServerFactoryCustomizerBeanPostProcessor::new);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,
Class<T> beanClass, Supplier<T> instanceSupplier)
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false)))
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
4.1 初始化WebServerFactory,添加connector等初始化器
在WebServerFactory构造时,借助bean后处理器WebServerFactoryCustomizerBeanPostProcessor
,会利用WebServerFactoryCustomizer
对应类型的bean对其进行初始化。内嵌tomcat时,可以通过application.yaml对tomcat容器的参数进行配置。外部tomcat时application.yaml关于tomcat的配置不生效
- TomcatWebSocketServletWebServerCustomizer,添加websocket ServerEndpoint相关处理器
- ServletWebServerFactoryCustomizer,主要是将yaml中配置的端口,设置到factory里。
- TomcatServletWebServerFactoryCustomizer,设置重定向相关。
- TomcatWebServerFactoryCustomizer,tomcat特定配置,核心配置,如线程池大小,访问日志,连接大小,请求头大小,错误路径等。
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.web.server;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.util.LambdaSafe;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.Assert;
/**
* @link BeanPostProcessor that applies all @link WebServerFactoryCustomizer beans
* from the bean factory to @link WebServerFactory beans.
*
* @author Dave Syer
* @author Phillip Webb
* @author Stephane Nicoll
* @since 2.0.0
*/
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware
private ListableBeanFactory beanFactory;
private List<WebServerFactoryCustomizer<?>> customizers;
@Override
public void setBeanFactory(BeanFactory beanFactory)
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
"WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");
this.beanFactory = (ListableBeanFactory) beanFactory;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
if (bean instanceof WebServerFactory)
postProcessBeforeInitialization((WebServerFactory) bean);
return bean;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
return bean;
@SuppressWarnings("unchecked")
private void postProcessBeforeInitialization(WebServerFactory webServerFactory)
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
private Collection<WebServerFactoryCustomizer<?>> getCustomizers()
if (this.customizers == null)
// Look up does not include the parent context
this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
return this.customizers;
@SuppressWarnings( "unchecked", "rawtypes" )
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans()
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
4.2 创建tomcat
主要是配置tomcat,类似server.xml,构建Connector,配置引擎。将4.1里设置到factory的初始化器,真正应用到tomcat配置上,例如最大线程数。
// org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
@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());
for (LifecycleListener listener : this.serverLifecycleListeners)
tomcat.getServer().addLifecycleListener(listener);
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);
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
// Needs to be protected so it can be used by subclasses
protected void customizeConnector(Connector connector)
int port = Math.max(getPort(), 0);
// 设置端口到connector
connector.setPort(port);
if (StringUtils.hasText(getServerHeader()))
connector.setProperty("server", getServerHeader());
if (connector.getProtocolHandler() instanceof AbstractProtocol)
customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
invokeProtocolHandlerCustomizers(connector.getProtocolHandler());
if (getUriEncoding() != null)
connector.setURIEncoding(getUriEncoding().name());
// Don't bind to the socket prematurely if ApplicationContext is slow to start
connector.setProperty("bindOnInit", "false");
if (getHttp2() != null && getHttp2().isEnabled())
connector.addUpgradeProtocol(new Http2Protocol());
if (getSsl() != null && getSsl().isEnabled())
customizeSsl(connector);
TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
compression.customize(connector);
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers)
// 设置线程池,请求头大小等
customizer.customize(connector);
protected void configureContext(Context context, ServletContextInitializer[] initializers)
// 添加初始化器
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext)
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
// TomcatStarter实现了ServletContainerInitializer,启动后会触发
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);
configureCookieProcessor(context);
new DisableReferenceClearingContextCustomizer().customize(context);
for (String webListenerClassName : getWebListenerClassNames())
context.addApplicationListener(webListenerClassName);
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers)
customizer.customize(context);
/**
* Factory method called to create the @link TomcatWebServer. Subclasses can
* override this method to return a different @link TomcatWebServer or apply
* additional processing to the Tomcat server.
* @param tomcat the Tomcat server.
* @return a new @link TomcatWebServer instance
*/
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
4.3 ServletContextInitializer配置tomcat
在TomcatWebServer的实例化过程的最后一步,会调用tomcat.start()方法,触发initializer初始化servlet,内嵌tomcat由TomcatStarter触发,因为TomcatStarter实现了ServletContainerInitializer,且也添加到了Tomcat上。外部tomcat时ApplicationContext#onRefresh直接调用这里,这里统一讲解。
// TomcatWebServer
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown)
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
initialize();
private void initialize() throws WebServerException
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor)
try
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) ->
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType()))
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
);
// Start the server to trigger initialization listeners
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
catch (NamingException ex)
// Naming is not enabled. Continue
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
catch (Exception ex)
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
其中,有个initializer,在TomcatServletWebServerFactory创建tomcat server时传入,是个lambda表达式适配的,也就是:
// ServletWebServerApplicationContext
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer()
return this::selfInitialize;
private void selfInitialize(ServletContext servletContext) throws ServletException
prepareWebApplicationContext(servletContext);
// 设置servletContext的root app context属性
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 核心,从beanfactory,找到所有的ServletContextInitializer,并注册到ServletContext中。
for (ServletContextInitializer beans : getServletContextInitializerBeans())
beans.onStartup(servletContext);
/**
* Returns @link ServletContextInitializers that should be used with the embedded
* web server. By default this method will first attempt to find
* @link ServletContextInitializer, @link Servlet, @link Filter and certain
* @link EventListener beans.
* @return the servlet initializer beans
*/
protected Collection<ServletContextInitializer> getServletContextInitializerBeans()
return new ServletContextInitializerBeans(getBeanFactory());
ServletContextInitializerBeans会加载ServletContextInitializer类型的bean。在spring boot中,实现了一个基类,也就是RegistrationBean
,实现了ServletContextInitializer,底层抽象了配置一个对象到servletContext的过程,大部分servlet/filter都可以使用RegistrationBean
相应子类实现注册。
@SuppressWarnings("varargs")
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes)
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// (1) 加载所有ServletContextInitializer类型的bean,并设置到initializers中
addServletContextInitializerBeans(beanFactory);
// (2) 添加适配类
addAdaptableBeans(beanFactory);
// 对initializers进行排序,外层会全部应用到initializer.onStartUp(serverltContext)
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
RegistrationBean继承体系,onStartUp分为两部,先添加到serveltContext,然后使用返回的registration对添加的对象做进一步配置,例如配置映射路径
public abstract class RegistrationBean implements ServletContextInitializer, Ordered
private static final Log logger = LogFactory.getLog(RegistrationBean.class);
private int order = Ordered.LOWEST_PRECEDENCE;
private boolean enabled = true;
@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(description, servletContext);
/**
* Return a description of the registration. For example "Servlet resourceServlet"
* @return a description of the registration
*/
protected abstract String getDescription();
/**
* Register this bean with the servlet context.
* @param description a description of the item being registered
* @param servletContext the servlet context
*/
protected abstract void register(String description, ServletContext servletContext);
/**
* Flag to indicate that the registration is enabled.
* @param enabled the enabled to set
*/
public void setEnabled(boolean enabled)
this.enabled = enabled;
/**
* Return if the registration is enabled.
* @return if enabled (default @code true)
*/
public boolean isEnabled()
return this.enabled;
/**
* Set the order of the registration bean.
* @param order the order
*/
public void setOrder(int order)
this.order = order;
/**
* Get the order of the registration bean.
* @return the order
*/
@Override
public int getOrder()
return this.order;
public abstract class DynamicRegistrationBean<D extends Registration.Dynamic> extends RegistrationBean
private static final Log logger = LogFactory.getLog(RegistrationBean.class);
private String name;
private boolean asyncSupported = true;
private Map<String, String> initParameters = new LinkedHashMap<>();
/**
* Set the name of this registration. If not specified the bean name will be used.
* @param name the name of the registration
*/
public void setName(String name)
Assert.hasLength(name, "Name must not be empty");
this.name = name;
/**
* Sets if asynchronous operations are supported for this registration. If not
* specified defaults to @code true.
* @param asyncSupported if async is supported
*/
public void setAsyncSupported(boolean asyncSupported)
this.asyncSupported = asyncSupported;
/**
* Returns if asynchronous operations are supported for this registration.
* @return if async is supported
*/
public boolean isAsyncSupported()
return this.asyncSupported;
/**
* Set init-parameters for this registration. Calling this method will replace any
* existing init-parameters.
* @param initParameters the init parameters
* @see #getInitParameters
* @see #addInitParameter
*/
public void setInitParameters(Map<String, String> initParameters)
Assert.notNull(initParameters, "InitParameters must not be null");
this.initParameters = new LinkedHashMap<>(initParameters);
/**
* Returns a mutable Map of the registration init-parameters.
* @return the init parameters
*/
public Map<String, String> getInitParameters()
return this.initParameters;
/**
* Add a single init-parameter, replacing any existing parameter with the same name.
* @param name the init-parameter name
* @param value the init-parameter value
*/
public void addInitParameter(String name, String value)
Assert.notNull(name, "Name must not be null");
this.initParameters.put(name, value);
@Override
protected final void register(String description, ServletContext servletContext)
D registration = addRegistration(description, servletContext);
if (registration == null)
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
configure(registration);
protected abstract D addRegistration(String description, ServletContext servletContext);
protected void configure(D registration)
// 添加到serveltContext后,可以使用返回的registration对添加的对象做进一步配置
registration.setAsyncSupported(this.asyncSupported);
if (!this.initParameters.isEmpty())
registration.setInitParameters(this.initParameters);
/**
* Deduces the name for this registration. Will return user specified name or fallback
* to convention based naming.
* @param value the object used for convention based names
* @return the deduced name
*/
protected final String getOrDeduceName(Object value)
return (this.name != null) ? this.name : Conventions.getVariableName(value);
按照序号(1),如果用户没有配置RegistrationBean的话,默认会添加三个bean:
4.3.1 添加默认的DispatcherServlet
其中DispatcherServletAutoConfiguration在用户未定义DispatcherSerlvet时自动注册DispatcherServletRegistrationBean
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
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(WebMvcProperties.class)
protected static class DispatcherServletConfiguration
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties)
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver)
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
@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)
// 使用spring.mvc.servlet.path作为servlet映射路径
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;
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata)
ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
List<String> dispatchServletBeans = Arrays
.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME))
return ConditionOutcome
.noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME))
return ConditionOutcome.noMatch(
message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
if (dispatchServletBeans.isEmpty())
return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());
return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")
.items(Style.QUOTE, dispatchServletBeans)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition extends SpringBootCondition
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
if (!outcome.isMatch())
return outcome;
return checkServletRegistration(beanFactory);
private ConditionOutcome checkDefaultDispatcherName(ConfigurableListableBeanFactory beanFactory)
boolean containsDispatcherBean = beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
if (!containsDispatcherBean)
return ConditionOutcome.match();
List<String> servlets = Arrays
.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
if (!servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME))
return ConditionOutcome.noMatch(
startMessage().found("non dispatcher servlet").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
return ConditionOutcome.match();
private ConditionOutcome checkServletRegistration(ConfigurableListableBeanFactory beanFactory)
ConditionMessage.Builder message = startMessage();
List<String> registrations = Arrays
.asList(beanFactory.getBeanNamesForType(ServletRegistrationBean.class, false, false));
boolean containsDispatcherRegistrationBean = beanFactory
.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
if (registrations.isEmpty())
if (containsDispatcherRegistrationBean)
return ConditionOutcome.noMatch(message.found("non servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
return ConditionOutcome.match(message.didNotFind("servlet registration bean").atAll());
if (registrations.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME))
return ConditionOutcome.noMatch(message.found("servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
if (containsDispatcherRegistrationBean)
return ConditionOutcome.noMatch(message.found("non servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
return ConditionOutcome.match(message.found("servlet registration beans").items(Style.QUOTE, registrations)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
private ConditionMessage.Builder startMessage()
return ConditionMessage.forCondition("DispatcherServlet Registration");
spring-boot-actuator-autoconfigure包里的WebMvcMetricsAutoConfiguration
引进WebMvcMetricsFilter registration
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter( MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
SimpleMetricsExportAutoConfiguration.class )
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@ConditionalOnBean(MeterRegistry.class)
@EnableConfigurationProperties(MetricsProperties.class)
public class WebMvcMetricsAutoConfiguration
private final MetricsProperties properties;
public WebMvcMetricsAutoConfiguration(MetricsProperties properties)
this.properties = properties;
@Bean
@ConditionalOnMissingBean(WebMvcTagsProvider.class)
public DefaultWebMvcTagsProvider webMvcTagsProvider(ObjectProvider<WebMvcTagsContributor> contributors)
return new DefaultWebMvcTagsProvider(this.properties.getWeb().getServer().getRequest().isIgnoreTrailingSlash(),
contributors.orderedStream().collect(Collectors.toList()));
@Bean
public FilterRegistrationBean<WebMvcMetricsFilter> webMvcMetricsFilter(MeterRegistry registry,
WebMvcTagsProvider tagsProvider)
ServerRequest request = this.properties.getWeb().getServer().getRequest();
WebMvcMetricsFilter filter = new WebMvcMetricsFilter(registry, tagsProvider, request.getMetricName(),
request.getAutotime());
FilterRegistrationBean<WebMvcMetricsFilter> registration = new FilterRegistrationBean<>(filter);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
return registration;
4.3.2 添加actuator endpoint servlet
ServletEndpointRegistrar是actuator的servelt注册器,其不走上面RegistrationBean,而是直接继承了ServletContextInitializer
。默认每个endpoint会单独注册成一个servlet。ServletEndpointRegistrar实际由ServletEndpointManagementContextConfiguration引进注册成bean,涉及到endpoint的实现原理,另外讲解。
// ServletEndpointRegistrar
private void register(ServletContext servletContext, ExposableServletEndpoint endpoint)
String name = endpoint.getEndpointId().toLowerCaseString() + "-actuator-endpoint";
String path = this.basePath + "/" + endpoint.getRootPath();
String urlMapping = path.endsWith("/") ? path + "*" : path + "/*";
EndpointServlet endpointServlet = endpoint.getEndpointServlet();
Dynamic registration = servletContext.addServlet(name, endpointServlet.getServlet());
registration.addMapping(urlMapping);
registration.setInitParameters(endpointServlet.getInitParameters());
registration.setLoadOnStartup(endpointServlet.getLoadOnStartup());
logger.info("Registered '" + path + "' to " + name);
4.3.3 适配servlet/filter/listener类型的bean为RegistrationBean,并添加到servletContext
在上面第(2)步中,还会添加servlet/filter类型的bean,包装为RegistrationBean。
//org.springframework.boot.web.servlet.ServletContextInitializerBeans#addAdaptableBeans
protected void addAdaptableBeans(ListableBeanFactory beanFactory)
// multiconfig,上传附件,
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
// 这里会在beanFactory里找Servlet的bean,排除已经被使用的DispatcherServlet,也就是source,包装为Registration,并带上multipartConfig配置
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
// 将所有Filter类型的bean适配成FilterRegistrationBean
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
// 将所有支持的listener适配成bean
for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes())
addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
其中附件配置时默认自动配置,
public class MultipartAutoConfiguration
private final MultipartProperties multipartProperties;
public MultipartAutoConfiguration(MultipartProperties multipartProperties)
this.multipartProperties = multipartProperties;
@Bean
@ConditionalOnMissingBean( MultipartConfigElement.class, CommonsMultipartResolver.class )
public MultipartConfigElement multipartConfigElement()
return this.multipartProperties.createMultipartConfig();
filter有三个,主要是字符编码过滤器OrderedCharacterEncodingFilter
,由HttpEncodingAutoConfiguration
注入。RequestContextFilter主要是暴露ServletRequestAttributes到线程变量,也就是httpServletRequest和httpServletResponse,由WebMvcAutoConfiguration
注入。
将下面默认有7个 listener bean适配成ServletListenerRegistrationBean,默认没有。
4.3.4 注册到servletContext
对于DispatcherServlet,DispatcherServletRegistrationBean会将其urlmapping/loadStartUp顺序/multipartConfig配置到servletContext上去。
5. spring容器启动后,注册服务实例到consul
在上面序号(3)中,创建嵌入tomcat成功(注意是内嵌tomcat),会手动注册两个bean,两个bean均实现了SmartLifecycle
接口,该接口属于spring实现的生命周期接口,当前spring容器单例加载完成后和关闭前,会分别触发对应的start和stop方法。如果有多个SmartLifecycle
接口,按照phase从小到大调用start方法,按照phase从大到调用stop方法。注意不同版本的spring boot实现不同,但是整体是在spring容器单例加载完成后和关闭前触发调用。
class WebServerStartStopLifecycle implements SmartLifecycle
private final ServletWebServerApplicationContext applicationContext;
private final WebServer webServer;
private volatile boolean running;
WebServerStartStopLifecycle(ServletWebServerApplicationContext applicationContext, WebServer webServer)
this.applicationContext = applicationContext;
this.webServer = webServer;
@Override
public void start()
this.webServer.start();
this.running = true;
this.applicationContext
.publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
@Override
public void stop()
this.webServer.stop();
@Override
public boolean isRunning()
return this.running;
@Override
public int getPhase()
return Integer.MAX_VALUE - 1;
/**
* @link SmartLifecycle to trigger @link WebServer graceful shutdown.
*
* @author Andy Wilkinson
* @since 2.5.0
*/
public final class WebServerGracefulShutdownLifecycle implements SmartLifecycle
/**
* @link SmartLifecycle#getPhase() SmartLifecycle phase in which graceful shutdown
* of the web server is performed.
*/
public static final int SMART_LIFECYCLE_PHASE = SmartLifecycle.DEFAULT_PHASE;
private final WebServer webServer;
private volatile boolean running;
/**
* Creates a new @code WebServerGracefulShutdownLifecycle that will gracefully shut
* down the given @code webServer.
* @param webServer web server to shut down gracefully
*/
public WebServerGracefulShutdownLifecycle(WebServer webServer)
this.webServer = webServer;
@Override
public void start()
this.running = true;
@Override
public void stop()
throw new UnsupportedOperationException("Stop must not be invoked directly");
@Override
public void stop(Runnable callback)
this.running = false;
this.webServer.shutDownGracefully((result) -> callback.run());
@Override
public boolean isRunning()
return this.running;
@Override
public int getPhase()
return SMART_LIFECYCLE_PHASE;
WebServerStartStopLifecycle
在spring单例刷新完毕后,会发送ServletWebServerInitializedEvent
事件,spring cloud利用这个事件,实现了注册当前服务实例到注册中心的功能。具体可以看spring-cloud-common的AbstractAutoServiceRegistration
。具体可见spring cloud demo,里面的consumer模块。
// org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration
public void onApplicationEvent(WebServerInitializedEvent event)
this.bind(event);
/** @deprecated */
@Deprecated
public void bind(WebServerInitializedEvent event)
ApplicationContext context = event.getApplicationContext();
if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace()))
// 设置端口,注册的端口从这里获取的
this.port.compareAndSet(0, event.getWebServer().getPort());
this.start();
serviceId为spring.application.name,端口为tomcat端口。如果有management端口,一般也会进行注册。
spring boot启动加载tomcat原理深度剖析