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,也就是TomcatServletWebServerFactoryServletWebServerFactoryConfiguration类由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原理深度剖析

springboot项目打成war后部署到tomcat,Tomcat启动时spring boot未加载

Spring Boot内嵌Tomcat原理

eclipse中tomcat无法加载spring boot

Spring Boot如何启动嵌入式Tomcat?

Spring Boot如何使用内嵌式的Tomcat和Jetty?