SpringBoot源碼:啟動內置Tomcat和加載DispatcherServlet原理


本文基於springboot 2.2.x分支源碼。

相關的自動配置類

關於Servlet、web內容在org.springframework.boot.autoconfigure.web.servlet包下面:

image-20211215184240007

該包下面主要有四個自動配置類:DispatcherServletAutoConfigurationHttpEncodingAutoConfigurationServletWebServerFactoryAutoConfigurationWebMvcAutoConfiguration

  • DispatcherServletAutoConfiguration:該類主要做DispatcherServlet相關的自動配置
  • HttpEncodingAutoConfiguration:該類主要做http字符編碼相關的配置
  • ServletWebServerFactoryAutoConfiguration:該類做的是Servlet web容器工廠相關配置
  • WebMvcAutoConfiguration:該類做的是springvc相關的配置,例如配置一些消息轉換器、視圖解析器、包括HandlerAdapter、HandlerMapping等相關配置

因為我們要了解springboot啟動內置tomcat以及加載DispatcherServlet的原理,就需要深入DispatcherServletAutoConfigurationServletWebServerFactoryAutoConfiguration類了。

ServletWebServerFactoryAutoConfiguration

先看該類的注解:

@Configuration(proxyBeanMethods = false) //配置類
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//自動配置順序
@ConditionalOnClass(ServletRequest.class)//類路徑有ServletRequest類
@ConditionalOnWebApplication(type = Type.SERVLET)//SERVLET的web環境
@EnableConfigurationProperties(ServerProperties.class)//使ServerProperties生效
//導入四個組件
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    
        //導入ServletWebServerFactoryCustomizer組件,主要對ServletWebServerFactory做定制化配置
        @Bean
	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
		return new ServletWebServerFactoryCustomizer(serverProperties);
	}

        //導入TomcatServletWebServerFactoryCustomizer組件,主要對TomcatServletWebServerFactory做定制化配置
	@Bean
	@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
	public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
			ServerProperties serverProperties) {
		return new TomcatServletWebServerFactoryCustomizer(serverProperties);
	}
 	//其他方法省略   
}

先看ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar

該類實現了ImportBeanDefinitionRegistrar,所以在ConfigurationClassPostProcessor解析配置類的時候,自動調用該接口的registerBeanDefinitions方法,

這里的BeanPostProcessorsRegistrar在該方法中注冊了兩個bean的定義信息:一個是ErrorPageRegistrarBeanPostProcessor,這個暫且不用管,主要是給ErrorPageRegistry中注冊錯誤頁的。

	public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
                //留下主要方法,其他方法省略
		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
			if (this.beanFactory == null) {
				return;
			}
			registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
					WebServerFactoryCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}
		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
			if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}
	}

另一個是注冊的bean定義信息是WebServerFactoryCustomizerBeanPostProcessor,這個類就比較有用了,看我在代碼中的注釋。

//實現了BeanPostProcessor,會攔截指定bean的創建
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
	private ListableBeanFactory beanFactory;
	private List<WebServerFactoryCustomizer<?>> customizers;
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //當創建的bean是WebServerFactory時,會調用postProcessBeforeInitialization(WebServerFactory) bean)
		if (bean instanceof WebServerFactory) {
			postProcessBeforeInitialization((WebServerFactory) bean);
		}
		return bean;
	}
	@SuppressWarnings("unchecked")
	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        //getCustomizers():拿到容器中所有注冊的WebServerFactoryCustomizer組件
		LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
				.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
            	//依次執行WebServerFactoryCustomize的customize方法,該方法主要是對WebServerFactory工廠做一些定制化的工作(配置)
				.invoke((customizer) -> customizer.customize(webServerFactory));
	}
	//其他方法省略。。。
}

再回來看ServletWebServerFactoryAutoConfiguration,還導入了其他三個組件。

  • ServletWebServerFactoryConfiguration.EmbeddedTomcat

  • ServletWebServerFactoryConfiguration.EmbeddedJetty

  • ServletWebServerFactoryConfiguration.EmbeddedUndertow

默認情況,只有EmbeddedTomcat會生效,看它的代碼:導入了一個TomcatServletWebServerFactory類型的組件,傳入三個ObjectProvider類型參數,都是對Connector、Context、ProtocolHandler做定制化操作的,默認情況下這三個參數都沒有配置,如果想要配置,只需要自己在容器中導入TomcatConnectorCustomizer、TomcatContextCustomizer、TomcatProtocolHandlerCustomizer組件就行了。

記得這里導入了TomcatServletWebServerFactory,后面tomcat的創建就靠它。

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	static class EmbeddedTomcat {

		@Bean
		TomcatServletWebServerFactory tomcatServletWebServerFactory(
				ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
				ObjectProvider<TomcatContextCustomizer> contextCustomizers,
				ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
			TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
			factory.getTomcatConnectorCustomizers()
					.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
			factory.getTomcatContextCustomizers()
					.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
			factory.getTomcatProtocolHandlerCustomizers()
					.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
			return factory;
		}

	}

DispatcherServletAutoConfiguration

再看這個配置類,這里面導入了兩個比較重要的組件:

1.導入DispatcherServlet這個組件,注意這個類僅僅只是保存到IOC容器中,並沒有在tomcat中初始化

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//自動配置順序
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)//在ServletWebServerFactoryAutoConfiguration自動配置之后再自動配置
public class DispatcherServletAutoConfiguration {

	/**
	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/".
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

	/**
	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

	@Configuration(proxyBeanMethods = false)
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
	protected static class DispatcherServletConfiguration {

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
			dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
			return dispatcherServlet;
		}
		//
	}

	@Configuration(proxyBeanMethods = false)
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
				WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
					webMvcProperties.getServlet().getPath());
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
			multipartConfig.ifAvailable(registration::setMultipartConfig);
			return registration;
		}
	}
}

2.導入了DispatcherServletRegistrationBean組件,先看該類的繼承樹,看看它繼承了哪些關鍵組件。

image-20211216090939243

可以觀察到,該類實現了ServletContextInitializer接口,再看接口上的英文注釋。

Interface used to configure a Servlet 3.0+ {@link ServletContext context}

programmatically. Unlike {@link WebApplicationInitializer}, classes that implement this

interface (and do not implement {@link WebApplicationInitializer}) will not be

detected by {@link SpringServletContainerInitializer} and hence will not be

automatically bootstrapped by the Servlet container.

This interface is designed to act in a similar way to

{@link ServletContainerInitializer}, but have a lifecycle that's managed by Spring and

not the Servlet container.

For configuration examples see {@link WebApplicationInitializer}.

大致含義:這個接口不像WebApplicationInitializer一樣被SpringServletContainerInitializer自動探測,因此不會自動被servlet容器啟動。但是它仍有着被spring管理的生命周期。

斷點調試

onRefresh

springboot啟動tomcat時候,日志會打印tomcat啟動的日志。我們只要在springboot 的run方法上打上斷點,不難找到tomcat啟動在哪里。

SpringApplication類的run方法中,這里調用了refreshContext,這一步是刷新ioc容器,這里斷點一過,tomcat啟動日志就打印了,所以tomcat就在這里面。

image-20211216092442970

走到經典的容器刷新方法,斷點一走過onRefresh方法,tomcat啟動日志就打印了,所以這里就是tomcat啟動的地方。

image-20211216092717329

createWebServer

進入onRefresh方法,先調用父類的onRefresh方法,然后調用createWebServer方法:

	@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

再看createWebServer方法:

	private void createWebServer() {
                //默認情況下,this.webServer和servletContext都為null
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
                        //獲取WebServerFactory
			ServletWebServerFactory factory = getWebServerFactory();
                        //從WebServerFactory中獲取webServer
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
                //初始化相關屬性
		initPropertySources();
	}

那么重點就是ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());

getWebServerFactory()

先看這個getWebServerFactory()方法:

	protected ServletWebServerFactory getWebServerFactory() {
		// Use bean names so that we don't consider the hierarchy
		String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
		if (beanNames.length == 0) {
			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
					+ "ServletWebServerFactory bean.");
		}
		if (beanNames.length > 1) {
			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
					+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
		}
		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
	}

第一步:從容器中獲取ServletWebServerFactory組件名,默認情況下,只有在ServletWebServerFactoryAutoConfiguration自動配置的TomcatServletWebServerFactory

第二步:校驗組件名,只有一個組件名的時候,不會拋出異常,如果找不到組件或者組件有多個都會拋出ApplicationContextException

第三步:從容器中獲取到ServletWebServerFactory組件,默認就是TomcatServletWebServerFactory

getSelfInitializer()

先看getSelfInitializer(),這個方法比較重要:內部是一個selfInitialize回調方法,並不會立馬執行,這里先提一嘴,后面會說到。

	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
	}

	//同時這個selfInitialize方法不會立刻調用,只是作為一個回調方法,當ServletContextInitializer.onStartUp調用時,才會被調用
	private void selfInitialize(ServletContext servletContext) throws ServletException {
                //准備WebApplicationContext,主要做一些校驗和設置屬性
		prepareWebApplicationContext(servletContext);
                //設置ApplicationScope
		registerApplicationScope(servletContext);
                //注冊一些關於Servlet環境的組件
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
                //getServletContextInitializerBeans(),主要是創建了一個ServletContextInitializerBeans集合
                //對ServletContextInitializerBeans集合遍歷,是對它內部的sortedList進行遍歷,里面存放了容器中的所有ServletContextInitializer
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                        //調用所有的ServletContextInitializer的onStartup方法
			beans.onStartup(servletContext);
		}
	}

對於這種寫法,可能看起來有點費解,因為getSelfInitializer()的返回值是一個ServletContextInitializer,同時它也是一個函數式接口,所以等價於下面這種寫法:

	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return servletContext -> {
			prepareWebApplicationContext(servletContext);
			registerApplicationScope(servletContext);
			WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
			for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
				beans.onStartup(servletContext);
			}
		};
	}

getWebServer

再看getWebServer方法:

	@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
                //new了一個tomcat
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
                //設置baseDir
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		connector.setThrowOnFailure(true);
                //添加一個connector
		tomcat.getService().addConnector(connector);
                //自定義connector
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
                //配置引擎
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
                //准備Context
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}

看prepareContext方法:

	protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
		File documentRoot = getValidDocumentRoot();
                //創建TomcatEmbeddedContext
		TomcatEmbeddedContext context = new TomcatEmbeddedContext();
		if (documentRoot != null) {
			context.setResources(new LoaderHidingResourceRoot(context));
		}
                //設置一些基本屬性
		context.setName(getContextPath());
		context.setDisplayName(getDisplayName());
		context.setPath(getContextPath());
		File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
		context.setDocBase(docBase.getAbsolutePath());
		context.addLifecycleListener(new FixContextListener());
		context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
				: ClassUtils.getDefaultClassLoader());
		resetDefaultLocaleMapping(context);
		addLocaleMappings(context);
		try {
			context.setCreateUploadTargets(true);
		}
		catch (NoSuchMethodError ex) {
			// Tomcat is < 8.5.39. Continue.
		}
		configureTldPatterns(context);
                //設置WebappLoader
		WebappLoader loader = new WebappLoader();
		loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
		loader.setDelegate(true);
		context.setLoader(loader);
		if (isRegisterDefaultServlet()) {
			addDefaultServlet(context);
		}
		if (shouldRegisterJspServlet()) {
			addJspServlet(context);
			addJasperInitializer(context);
		}
                //增加LifecycleListener
		context.addLifecycleListener(new StaticResourceConfigurer(context));
                //合並Initializers,主要是添加一些其他的ServletContextInitializer
		ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
		host.addChild(context);
                //配置context
		configureContext(context, initializersToUse);
		postProcessContext(context);
	}

進入configureContext方法:

	protected void configureContext(Context context, ServletContextInitializer[] initializers) {
                //創建了TomcatStarter,這個實現了ServletContainerInitializer接口
                //對servlet3.0規范熟悉的知道,tomcat在servlet容器初始化的時候,會調用所有的ServletContainerInitializer接口的onStartup方法
		TomcatStarter starter = new TomcatStarter(initializers);
		if (context instanceof TomcatEmbeddedContext) {
			TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
			embeddedContext.setStarter(starter);
			embeddedContext.setFailCtxIfServletStartFails(true);
		}
                //添加到context中
		context.addServletContainerInitializer(starter, NO_CLASSES);
		for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
			context.addLifecycleListener(lifecycleListener);
		}
                //添加tomcat閥門
		for (Valve valve : this.contextValves) {
			context.getPipeline().addValve(valve);
		}
                //錯誤頁配置
		for (ErrorPage errorPage : getErrorPages()) {
			org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
			tomcatErrorPage.setLocation(errorPage.getPath());
			tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
			tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
			context.addErrorPage(tomcatErrorPage);
		}
		for (MimeMappings.Mapping mapping : getMimeMappings()) {
			context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
		}
		configureSession(context);
		new DisableReferenceClearingContextCustomizer().customize(context);
                //對TomcatContext進行自定義配置
		for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
			customizer.customize(context);
		}
	}

到這里,看了一大半,有人可能一臉蒙蔽,我是誰,我在哪兒?這里究竟做了哪些關鍵步驟?

我來解釋一下在getWebServer方法截至運行到prepareContext,springboot究竟做了哪些事情。

  • getSelfInitializer返回的是一個函數式接口,內部主要是拿到springboot中的所有ServletContextInitializer,並依次執行它的onStartup方法
  • 把這個函數式接口作為參數傳入到getWebServer方法中
  • getWebServer方法主要做了以下幾件事
    • 創建了一個Tomcat實例,設置了一些屬性
    • 為tomcat的Host配置Context
    • 創建了一個TomcatStarter並把上面的getSelfInitializer返回的函數式接口設置到它的成員變量中,TomcatStarter實際上是ServletContainerInitializer,tomcat在servlet容器初始化的時候,會調用所有的ServletContainerInitializer接口的onStartup方法
    • 然后把TomcatStarter添加到context中

斷點繼續往下走,走到getTomcatWebServer(tomcat),進去:直接new了一個TomcatWebServer,並把前面創建的tomcat傳進去。

	protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
		return new TomcatWebServer(tomcat, getPort() >= 0);
	}

斷點繼續往里面走:設置了屬性后,直接調用initialize方法:

	public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		initialize();
	}

initialize方法如下:終於在這一步,看到了tomcat.strart()方法,至此,tomcat終於啟動。

	private void initialize() throws WebServerException {
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();

				Context context = findContext();
				context.addLifecycleListener((event) -> {
					if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
						removeServiceConnectors();
					}
				});
				//tomcat啟動
				this.tomcat.start();
				rethrowDeferredStartupExceptions();

				try {
					ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
				}
				catch (NamingException ex) {
				}
				startDaemonAwaitThread();
			}
			catch (Exception ex) {
				stopSilently();
				destroySilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

TomcatStarter.onStartup

tomcat.start()方法啟動后,因為TomcatStarter實現了ServletContainerInitializer接口,在前面手動調用了context.addServletContainerInitializer(starter, NO_CLASSES);,把tomcatStart添加到context中,所以這里的TomcatStarter並不是通過SPI機制加載到tomcat中,這里與springmvc處理不一樣。

tomcat啟動后,會執行ServletContainerInitializer的onStartUp方法,所以TomcatStarter會被調用:

	@Override
	public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
		try {
			for (ServletContextInitializer initializer : this.initializers) {
				initializer.onStartup(servletContext);
			}
		}
		catch (Exception ex) {
			this.startUpException = ex;
			// Prevent Tomcat from logging and re-throwing when we know we can
			// deal with it in the main thread, but log for information here.
			if (logger.isErrorEnabled()) {
				logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
						+ ex.getMessage());
			}
		}
	}

這里的this.initializers有三個,我們只要管前面傳進來的getSelfInitializer()方法:這里前面提到的selfInitialize方法才會被調用

	private void selfInitialize(ServletContext servletContext) throws ServletException {
                //准備WebApplicationContext,主要做一些校驗和設置屬性
		prepareWebApplicationContext(servletContext);
                //設置ApplicationScope
		registerApplicationScope(servletContext);
                //注冊一些關於Servlet環境的組件
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
                //getServletContextInitializerBeans(),主要是創建了一個ServletContextInitializerBeans集合
                //對ServletContextInitializerBeans集合遍歷,是對它內部的sortedList進行遍歷,里面存放了容器中的所有ServletContextInitializer
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                        //調用所有的ServletContextInitializer的onStartup方法
			beans.onStartup(servletContext);
		}
	}

這里getServletContextInitializerBeans()返回了四個ServletContextInitializer:

image-20211216104613197

其中第一個,就是配置類中配置的DispatcherServletRegistrationBean,而springboot通過該類注冊了DispatcherServlet,我們接下來來看springboot是怎么注冊的。

注冊DispatcherServlet

進入DispatcherServletRegistrationBean的onstart方法,直接進到它的父類RegistrationBean:

	@Override
	public final void onStartup(ServletContext servletContext) throws ServletException {
		String description = getDescription();
		if (!isEnabled()) {
			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
			return;
		}
                //調用register
		register(description, servletContext);
	}

斷點進入register方法

@Override
protected final void register(String description, ServletContext servletContext) {
    //執行addRegistration
   D registration = addRegistration(description, servletContext);
   if (registration == null) {
      logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
      return;
   }
    //調用configure
   configure(registration);
}

進入addRegistration,終於走進DispatcherServletRegistrationBean類中:直接調用servlet原生方法

	@Override
	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
		String name = getServletName();
                //添加servlet
		return servletContext.addServlet(name, this.servlet);
	}

這里this.servlet也就是DispatcherServlet,是在前面DispatcherServletAutoConfiguration 自動配置類中傳入到DispatcherServletRegistrationBean類中的。

方法出來后,走進configure方法:這里會給ServletRegistration.Dynamic設置一些屬性,這也是javax.servlet的原生方法。

	@Override
	protected void configure(ServletRegistration.Dynamic registration) {
		super.configure(registration);
		String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
		if (urlMapping.length == 0 && this.alwaysMapUrl) {
			urlMapping = DEFAULT_MAPPINGS;
		}
		if (!ObjectUtils.isEmpty(urlMapping)) {
			registration.addMapping(urlMapping);
		}
		registration.setLoadOnStartup(this.loadOnStartup);
		if (this.multipartConfig != null) {
			registration.setMultipartConfig(this.multipartConfig);
		}
	}

至此,tomcat啟動了,DispatcherServlet注冊到tomcat中,同時RegistrationBean也會注冊javax原生的監聽器和過濾器,具體實現是ServletListenerRegistrationBeanFilterRegistrationBean,這里不再詳細講解。

流程圖

流程圖如下:

springboot啟動tomcat原理


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM