SpringBoot中Tomcat和SpringMVC整合源碼分析
概述
?SpringBoot中集成官方的第三方組件是通過在POM文件中添加組件的starter的Maven依賴來完成的。添加相關(guān)的Maven依賴之后,會(huì)引入具體的jar包,在SpringBoot啟動(dòng)的時(shí)候會(huì)根據(jù)默認(rèn)自動(dòng)裝配的配置類的注入條件判斷是否注入該自動(dòng)配置類到Spring容器中。自動(dòng)配置類中會(huì)創(chuàng)建具體的第三方組件需要的類 。Tomcat和SpringMVC都是通過這樣的方式進(jìn)行集成的。SpringBoot出現(xiàn)之前SpringMVC項(xiàng)目是直接部署在Tomcat服務(wù)器中的,Tomcat是一個(gè)符合Servlet標(biāo)準(zhǔn)的Web服務(wù)器,Tomcat單獨(dú)作為一個(gè)可安裝軟件。這種方式下Tomcat是一個(gè)完整獨(dú)立的web服務(wù)器,SpringMVC項(xiàng)目不能脫離Web服務(wù)器直接運(yùn)行,需要先部署在Tomcat服務(wù)器的目錄中才能運(yùn)行。SpringBoot出現(xiàn)之后改變了這種方式,我們可以直接運(yùn)行SpringBoot項(xiàng)目,因?yàn)镾pringBoot中可以內(nèi)嵌tomcat服務(wù)器,而tomcat也是java開發(fā)的。在啟動(dòng)SpringBoot項(xiàng)目的時(shí)候直接創(chuàng)建tomcat服務(wù)并且把SpringMVC的項(xiàng)目部署在tomcat服務(wù)中。
一、自動(dòng)裝配原理
?在SpringBoot自動(dòng)裝配的程中,會(huì)加載/META-INF/factories文件中的以EnableAutoConfiguration為Key的配置類的字符串?dāng)?shù)組,在該類中會(huì)根據(jù)具體引入的服務(wù)器類進(jìn)行創(chuàng)建具體的服務(wù)器對(duì)象。這些字符串?dāng)?shù)組是否真正加載進(jìn)Spring容器,需要經(jīng)過兩方面的判斷,
? 1.根據(jù)META-INF/spring-autoconfigure-metadata.properties文件中配置的規(guī)則判斷是否需要過濾。
?2.在org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass中會(huì)根據(jù)自動(dòng)裝配類的條件注解來判斷是否進(jìn)行自動(dòng)加載。如果條件注解全部校驗(yàn)成功才會(huì)加載該配置類。
兩種情況的底層校驗(yàn)邏輯都是一樣的,都是通過調(diào)用OnWebApplicationCondition、OnClassCondition、OnBeanCondition的match方法進(jìn)行判斷。但是在processConfigurationClass方式的檢查類型會(huì)更多,比如ConditionalOnMissingBean 等條件的檢查。
條件注解 | 注解備注 |
---|---|
OnWebApplicationCondition | 判斷是否符合服務(wù)器類型 |
OnClassCondition | 判斷項(xiàng)目中是否存在配置的類 |
OnBeanCondition | 判斷Spring容器中是否注入配置的類 |
二、內(nèi)嵌式Tomcat注入
2.1自動(dòng)注入配置類分析
1.ServletWebServerFactoryAutoConfiguration 配置類分析
?1.META-INF/factories文件中以EnableAutoConfiguration為Key的配置類的字符串?dāng)?shù)組,其中 ServletWebServerFactoryAutoConfiguration 為創(chuàng)建Web服務(wù)器的自動(dòng)配置類,根據(jù)META-INF/spring-autoconfigure-metadata.properties文件中配置的規(guī)則判斷是否需要過濾,該文件關(guān)于ServletWebServerFactoryAutoConfiguration的配置描述如下,
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnClass=javax.servlet.ServletRequest org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=SERVLET org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.AutoConfigureOrder=-2147483648
此配置說明需要在當(dāng)前的項(xiàng)目中有javax.servlet.ServletRequest類并且WebApplication是SERVLET容器,才不會(huì)過濾。
2.根據(jù)自動(dòng)裝配類的條件注解來判斷是否進(jìn)行自動(dòng)加載,ServletWebServerFactoryAutoConfiguration 配置類的條件注解如下,
@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 判斷是否有 ServletRequest 類 @ConditionalOnClass(ServletRequest.class) // 判斷 WebApplication 是 SERVLET 容器 @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class)
此配置說明需要在當(dāng)前的項(xiàng)目中有javax.servlet.ServletRequest類并且WebApplication是SERVLET容器,才會(huì)進(jìn)行ServletWebServerFactoryAutoConfiguration 類的加載。
2.2注入邏輯具體分析
tomcat的starter依賴配置代碼如下,
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.2.5.RELEASE</version> <scope>compile</scope> </dependency>
添加Maven依賴配置后,會(huì)引入tomcat-embed-corejar包,其中有ServletRequest接口。代碼截圖如下,
SpringBoot的在創(chuàng)建服務(wù)器類型的時(shí)候是Servlet的,在創(chuàng)建SpringApplication的run方法中會(huì)調(diào)用createApplicationContext方法創(chuàng)建具體的AnnotationConfigServletWebServerApplicationContext類,該類繼承了org.springframework.web.context.support.GenericWebApplicationContext類。OnWebApplicationCondition條件注解中就是通過判斷是否有GenericWebApplicationContext類來檢查是否是SERVLET類型的。
3.所 以添加完tomcat的starter依賴配置后,ServletWebServerFactoryAutoConfiguration所有的條件注解都會(huì)匹配成功 ,即會(huì)自動(dòng)加載 ServletWebServerFactoryAutoConfiguration 自動(dòng)裝配類。該類會(huì)通過@Import注解導(dǎo)入3個(gè)內(nèi)嵌服務(wù)器的實(shí)現(xiàn)類,這3個(gè)實(shí)現(xiàn)類的服務(wù)器都是Servlet標(biāo)準(zhǔn)的。代碼如下,
// 導(dǎo)入具體服務(wù)器類 @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
三個(gè)Embeddedxxx表示相應(yīng)的服務(wù)器實(shí)現(xiàn)類,每個(gè)服務(wù)器類都有自己的條件注解實(shí)現(xiàn)不同的加載邏輯,如果這里注入了多個(gè)web服務(wù)器,在服務(wù)器啟動(dòng)的時(shí)候會(huì)報(bào)錯(cuò)。EmbeddedTomcat類的代碼如下,
@Configuration(proxyBeanMethods = false) #當(dāng)前工程下需要有Servlet、Tomcat UpgradeProtocol類 @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) #當(dāng)前的Spring容器類不能有ServletWebServerFactory類型的Bean,搜索的是當(dāng)前容器。 @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedTomcat { @Bean TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) { // 創(chuàng)建 TomcatServletWebServerFactory ,是產(chǎn)生TomcatServletWebServer的工廠類 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; } }
從以上代碼看出,EmbeddedTomcat配置類能加載注入的條件是當(dāng)前工程下需要有Servlet、Tomcat、 UpgradeProtocol3個(gè)類并且當(dāng)前Spring容器沒有注入ServletWebServerFactory類型的Bean。而這個(gè)三個(gè)類都在tomcat-embed-core.jar包中,并且Spring容器中之前沒有注入ServletWebServerFactory類型的Bean,所會(huì)自動(dòng)加載EmbeddedTomcat類。EmbeddedTomcat用來注入TomcatServletWebServerFactory到Spring容器,TomcatServletWebServerFactory的實(shí)現(xiàn)了ServletWebServerFactory接口,TomcatServletWebServerFactory可以創(chuàng)建具體的TomcatWebServer類。在SpringBoot啟動(dòng)的時(shí)候會(huì)從Spring容器中獲取ServletWebServerFactory類型的Bean。至此Tomcat的自動(dòng)裝置解析完了。
三、SpringMVC注入
?SpringMVC中最重要的組件是DispatchServlet,SpringMVC要在Servlet類型的Web服務(wù)器運(yùn)行,就要把Servlet添加到Web容器中,用Servlet來處理請(qǐng)求?;叵胍幌乱郧暗腟pringMVC,我們需要在web.xml配置文件中添加Servlet的配置,會(huì)默認(rèn)把SpringMVC的DispatchServlet配置到Servlet配置節(jié)點(diǎn)中,web.xm配置節(jié)點(diǎn)代碼如下,
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!--Web服務(wù)器一旦啟動(dòng),Servlet就會(huì)實(shí)例化創(chuàng)建對(duì)象,然后初始化(預(yù)備創(chuàng)建對(duì)象)--> <load-on-startup>1</load-on-startup> </servlet>
在SpringBoot中我們使用的是內(nèi)嵌式的Tomcat,那tomcat和SpringMVC的DispatchServlet是怎么關(guān)聯(lián)起來的?一起往下看,META-INF/factories文件中的以EnableAutoConfiguration為Key的配置類的字符串?dāng)?shù)組,其中 DispatcherServletAutoConfiguration 和 WebMvcAutoConfiguration 是SpringMVC中最為重要的兩個(gè)自動(dòng)配置類,DispatcherServletAutoConfiguration 用來注冊(cè) DispatcherServlet到 Spring容器,WebMvcAutoConfiguration 用來注入MVC的相關(guān)組件到Spring容器。
3.1自動(dòng)注入配置類分析
1.DispatcherServletAutoConfiguration 配置類分析
?1.1 根據(jù)META-INF/spring-autoconfigure-metadata.properties文件中配置的規(guī)則判斷是否需要過濾,該文件關(guān)于DispatcherServletAutoConfiguration 的配置描述如下,
#WebApplication是SERVLET容器 org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.ConditionalOnWebApplication=SERVLET #工程中需要引入 DispatcherServlet 類 org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.ConditionalOnClass=org.springframework.web.servlet.DispatcherServlet #容器注入在 ServletWebServerFactoryAutoConfiguration(服務(wù)器自動(dòng)配置類) 類之后, org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.AutoConfigureOrder=-2147483648
DispatcherServletAutoConfiguration 的邏輯為:需要在當(dāng)前的項(xiàng)目中有DispatcherServlet類并且WebApplication是SERVLET容器,才不會(huì)過濾掉。
?1.2 根據(jù)自動(dòng)裝配類的條件注解來判斷是否進(jìn)行自動(dòng)加載,DispatcherServletAutoConfiguration 配置類的條件注解如下,
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
此邏輯需要在當(dāng)前的項(xiàng)目中有DispatcherServlet類并且WebApplication是SERVLET容器,并且注入在 DispatcherServletAutoConfiguration 類之后,才會(huì)進(jìn)行DispatcherServletAutoConfiguration 類的加載。在DispatcherServletAutoConfiguration 中有兩個(gè)非常重要的內(nèi)部類,DispatcherServletConfiguration 和DispatcherServletRegistrationConfiguration ,這兩個(gè)內(nèi)部類也會(huì)根據(jù)條件注解加載來決定是否加載進(jìn)Spring容器中 。DispatcherServletConfiguration 主要用來注入 DispatcherServlet ;DispatcherServletRegistrationConfiguration 主要用來注入 DispatcherServletRegistrationBean,DispatcherServletRegistrationBean 用來包裝 DispatcherServlet 類,它實(shí)現(xiàn)了ServletContextInitializer接口,用來注冊(cè)Servlet對(duì)象到tomcat中的ServletContext對(duì)象。
2.DispatcherServletConfiguration 配置類分析
?DispatcherServletConfiguration 中有個(gè)DispatcherServlet類型的@Bean對(duì)象,會(huì)把DispatcherServlet注入到Spring容器中,DispatcherServletConfiguration 代碼如下,
@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; } }
以上條件注解代碼可知,將BeanName為 dispatcherServlet ,類型為DispatcherServlet 的Bean注入到容器中的條件為:先查找是否已經(jīng)容器中是否存在名稱 dispatcherServlet的Bean或者類型DispatcherServlet 的Bean,如果都不存并且當(dāng)前工程中有ServletRegistration類則將DispatcherServletConfiguration注入到Spring容器中,并且還會(huì)注入DispatcherServlet類。
3.DispatcherServletRegistrationConfiguration 配置類分析
?DispatcherServletRegistrationConfiguration 主要用來注入 DispatcherServletRegistrationBean,DispatcherServletRegistrationBean 用來包裝 DispatcherServlet 類,它實(shí)現(xiàn)了ServletContextInitializer接口,SpringBoot用DispatcherServletRegistrationBean 來注冊(cè)Servlet對(duì)象到Tomcat服務(wù)器中的ServletContext對(duì)象,在后面Tomcat啟動(dòng)的時(shí)候會(huì)講具體實(shí)現(xiàn)。DispatcherServletRegistrationConfiguration代碼如下,
@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; } }
以上條件注解代碼可知,將BeanName為 dispatcherServletRegistration,類型為DispatcherServletRegistrationBean的Bean注入到容器中的條件為:先查找是否已經(jīng)容器中是否存在名稱 dispatcherServletRegistration的Bean或者類型DispatcherServletRegistrationBean的Bean,如果都不存并且當(dāng)前工程中有ServletRegistration類則將DispatcherServletRegistrationConfiguration注入到Spring容器中,并且還會(huì)注入DispatcherServletRegistrationBean類。
4.WebMvcAutoConfiguration
?4. 1 根據(jù)META-INF/spring-autoconfigure-metadata.properties文件中配置的規(guī)則判斷是否需要過濾,該文件關(guān)于 WebMvcAutoConfiguration 的配置描述如下,
#工程中需要引入Servlet、WebMvcConfigurer、 DispatcherServlet類 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.ConditionalOnClass=javax.servlet.Servlet,org.springframework.web.servlet.config.annotation.WebMvcConfigurer,org.springframework.web.servlet.DispatcherServlet # 容器注入在 DispatcherServletAutoConfiguration、TaskExecutionAutoConfiguration、ValidationAutoConfiguration之 # 后 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureOrder=-2147483638
WebMvcAutoConfiguration的邏輯為:需要在當(dāng)前的項(xiàng)目中有Servlet、WebMvcConfigurer、DispatcherServlet3個(gè)類,才不會(huì)過濾掉。
?4.2 根據(jù)自動(dòng)裝配類的條件注解來判斷是否進(jìn)行自動(dòng)加載,WebMvcAutoConfiguration配置類的條件注解如下,
@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
此邏輯需要當(dāng)前的應(yīng)用程序?yàn)镾ERVLET類型,當(dāng)前的項(xiàng)目中有Servlet、DispatcherServlet、WebMvcConfigurer3個(gè)類、WebMvcConfigurationSupport沒有被注入到Spring容器、在 DispatcherServletAutoConfiguration 、TaskExecutionAutoConfiguration、ValidationAutoConfiguration 類注入到Spring容器之后,才會(huì)進(jìn)行WebMvcAutoConfiguration類的注入。在該中有個(gè)內(nèi)部類WebMvcAutoConfigurationAdapter ,主要用來注入 SpringMVC的組件,比如ViewResolver、LocaleResolver等。
3.2 注入邏輯具體分析
1.springmvc的starter依賴配置代碼如下
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.4.RELEASE</version> <scope>compile</scope> </dependency>
2.添加Maven依賴配置后,會(huì)引入spring-webmvc.jar包,其中有DispatcherServlet類、WebMvcConfigurer接口。而springmvc的自動(dòng)配置類在ServletWebServerFactoryAutoConfiguration配置類加載之后進(jìn)行加載,所以肯定會(huì)有Servlet的存在。
3.所以添加完springmvc的Maven依賴配置后,會(huì)自動(dòng)加載 DispatcherServletAutoConfiguration類和WebMvcAutoConfiguration類以及他們配置類中其他的配置類。至此SpringMVC的自動(dòng)裝置解析完了。
四、Tomcat與SpringMVC集成
1.在SpringApplication的run方法中,會(huì)進(jìn)行ApplicationContext的創(chuàng)建,然后會(huì)執(zhí)行 refreshContext 方法,其中的會(huì)執(zhí)行onRefresh方法,這個(gè)方法是由子類ServletWebServerApplicationContext實(shí)現(xiàn),ServletWebServerApplicationContext中onRefresh方法代碼如下,
protected void onRefresh() { super.onRefresh(); try { // 創(chuàng)建 web 服務(wù)器 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
2.createWebServer里面的邏輯主要是創(chuàng)建web服務(wù)器,主要代碼如下,
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); // 如果為 null,則創(chuàng)建 if (webServer == null && servletContext == null) { // 獲取WebServerFactory ServletWebServerFactory factory = getWebServerFactory(); // 獲取 webServer ,getSelfInitializer() 返回的是 函數(shù)式接口 // 是一個(gè) 實(shí)現(xiàn)了@FunctionalInterface 接口的列表 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(); }
3,創(chuàng)建服務(wù)器分為兩步,第一步獲取WebServerFactory 服務(wù)器工廠,第二步根據(jù)工廠獲取具體的web服務(wù)器。getWebServerFactory()主要邏輯為:先獲 BeanFactory ,然后從 BeanFactory取 ServletWebServerFactory 類型的 BeanNames數(shù)組,如果數(shù)組長度為 0,則拋異常,如果數(shù)組長度大于 1,則拋異常,最后根據(jù)第一個(gè)beanName和ServletWebServerFactory類型獲取容器中的Bean進(jìn)行返回。
4.getWebServerFactory方法源碼如下,
protected ServletWebServerFactory getWebServerFactory() { // 先獲 BeanFactory ,再從 BeanFactory取 ServletWebServerFactory 類型的 BeanNames String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); // 如果長度為 0,則拋異常 if (beanNames.length == 0) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean."); } // 如果長度大于 1,則拋異常 if (beanNames.length > 1) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } // 返回根據(jù)第一個(gè)beanName和ServletWebServerFactory類型獲取容器中的Bean return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
因?yàn)橹笆亲⑷肓薚omcat的TomcatServletWebServerFactory,這里返回的是前面注入到容器里面的 TomcatServletWebServerFactory,然后調(diào)用該類的getWebServer方法,傳入getSelfInitializer()方法參數(shù),這個(gè)時(shí)候不會(huì)進(jìn)行g(shù)etSelfInitializer()里面具體業(yè)務(wù)邏輯調(diào)用,而是生成實(shí)現(xiàn)了ServletContextInitializer接口的匿名類后傳入方法中。ServletContextInitializer接口代碼如下,
@FunctionalInterface public interface ServletContextInitializer { /** * Configure the given {@link ServletContext} with any servlets, filters, listeners * context-params and attributes necessary for initialization. * @param servletContext the {@code ServletContext} to initialize * @throws ServletException if any call against the given {@code ServletContext} * throws a {@code ServletException} */ void onStartup(ServletContext servletContext) throws ServletException; }
5.getSelfInitializer()方法返回的是函數(shù)表達(dá)式,不會(huì)立馬執(zhí)行方法內(nèi)容。當(dāng)tomcat服務(wù)器啟動(dòng)的時(shí)候會(huì)調(diào)用該匿名類的onStartup方法。getSelfInitializer方法中的主要邏輯為:找出實(shí)現(xiàn)了ServletContextInitializer 接口的類并注入容器 ,然后循環(huán)調(diào)用實(shí)現(xiàn)了ServletContextInitializer 接口的匿名類的onStartup()方法。在3.1 章節(jié)中注入了實(shí)現(xiàn)了ServletContextInitializer 接口的DispatcherServletRegistrationBean類,這里會(huì)調(diào)用DispatcherServletRegistrationBean的onStartup()方法,而DispatcherServletRegistrationBean中包含了3.1中注入的DispatcherServlet 類。DispatcherServletRegistrationBean的onStartup方法的主要邏輯為:將當(dāng)前類中的DispatcherServlet 添加到Tomcat 的 servletContext 中。getSelfInitializer關(guān)鍵代碼如下,
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { return this::selfInitialize; } private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); // 注入 servletContext 類型的 bean WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); // 注入實(shí)現(xiàn)了ServletContextInitializer 接口的類 ,并進(jìn)行循環(huán)調(diào)用onStartup for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }
上面的getServletContextInitializerBeans()方法主要邏輯為:創(chuàng)建一個(gè)ServletContextInitializerBeans類,它實(shí)現(xiàn)了AbstractCollection接口,可以作為集合直接遍歷。ServletContextInitializerBeans構(gòu)造方法主要邏輯為:在容器中獲取所有實(shí)現(xiàn)了ServletContextInitializer 接口的類添加到this.initializers中,排序后賦值給sortedList屬性。getServletContextInitializerBeans()和ServletContextInitializerBeans的構(gòu)造方法代碼如下,
//getServletContextInitializerBeans具體代碼 protected Collection<ServletContextInitializer> getServletContextInitializerBeans() { // 構(gòu)造ServletContextInitializerBeans對(duì)象,該對(duì)象實(shí)現(xiàn)了 AbstractCollection<ServletContextInitializer> 接口 // 是 ServletContextInitializer的 一個(gè)集合類。 return new ServletContextInitializerBeans(getBeanFactory()); } // ServletContextInitializerBeans 構(gòu)造方法 public ServletContextInitializerBeans(ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes) { // 初始化 initializers this.initializers = new LinkedMultiValueMap<>(); this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class); // 在beanFactory中查找實(shí)現(xiàn)了 ServletContextInitializer 接口的類 // 這里包括了 ServletRegistrationBean 、FilterRegistrationBean // DelegatingFilterProxyRegistrationBean、ServletListenerRegistrationBean 和其他。 // 添加到 initializers 集合中 addServletContextInitializerBeans(beanFactory); // 添加適配的 Bean addAdaptableBeans(beanFactory); // initializers 排序 List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream() .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE)) .collect(Collectors.toList()); // 給該集合可遍歷對(duì)象賦值 this.sortedList = Collections.unmodifiableList(sortedInitializers); // 返回 initializers logMappings(this.initializers); }
addServletContextInitializerBeans()方法中會(huì)找到所有實(shí)現(xiàn)了 ServletContextInitializer 接口的類,即會(huì)找到3.1章節(jié)中解析的DispatcherServletRegistrationBean類,都添加到 this.initializers 集合中。
?5.1DispatcherServletRegistrationBean的onStartup方法主要邏輯為:把當(dāng)前的DispacherServlet對(duì)象添加到從參數(shù)傳進(jìn)來的ServletContext對(duì)象中,onStartup具體代碼如下,
public final void onStartup(ServletContext servletContext) throws ServletException { String description = getDescription(); if (!isEnabled()) { logger.info(StringUtils.capitalize(description) + " was not registered (disabled)"); return; } // 注冊(cè) Servlet 到 tomcat的 servletContext中 register(description, servletContext); }
?5.2 register()方法是具體注冊(cè)當(dāng)前Servlet到tomcat的 servletContext中,代碼如下,
@Override protected final void register(String description, ServletContext servletContext) { // 添加當(dāng)前的 servlet 到 tomcat 的 servletContext 中 D registration = addRegistration(description, servletContext); if (registration == null) { logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)"); return; } configure(registration); }
?5.3 addRegistration方法是先獲取servlet名稱再注冊(cè)當(dāng)前Servlet到參數(shù)傳進(jìn)來的的servletContext參數(shù)中,代碼如下
@Override protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) { // 獲取 當(dāng)前對(duì)象的 servlet 名稱 String name = getServletName(); //添加當(dāng)前的 servlet 到 tomcat 的 servletContext 中 return servletContext.addServlet(name, this.servlet); }
?5.4總結(jié)一下,這個(gè)getSelfInitializer()很關(guān)鍵,主要邏輯為:把springmvc中的DispatcherServlet 添加到tomcat服務(wù)器中的servletContext對(duì)象中 。
6.getWebServer方法用來獲取WebServer,大概邏輯是:先創(chuàng)建Tomcat類,再初始化相關(guān)屬性,最后創(chuàng)建TomcatWebServer對(duì)象。具體的代碼如下,
public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } // 創(chuàng)建具體的Tomcat類 Tomcat tomcat = new Tomcat(); // 獲取主路徑,并且設(shè)置主路徑 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); // 創(chuàng)建連接器和協(xié)議 Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); // 添加連接器 tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); // 設(shè)置 host 的自動(dòng)部署屬性為 false tomcat.getHost().setAutoDeploy(false); // 配置 engine configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } // 初始化 TomcatEmbeddedContext ,并把 TomcatEmbeddedContext 添加至 host 中 // 用 initializersToUse 初始化 TomcatStarter ,并設(shè)置 TomcatEmbeddedContext 的Starter屬性為 TomcatStarter // initializersToUse 為函數(shù)式接口 ,即 實(shí)現(xiàn)了ServletContextInitializer接口 的匿名類 prepareContext(tomcat.getHost(), initializers); // 創(chuàng)建具體的 TomcatwebServer return getTomcatWebServer(tomcat); }
7.prepareContext()方法是創(chuàng)建WebServer前的準(zhǔn)備 ,主要邏輯為:創(chuàng)建 TomcatEmbeddedContext ,并把 TomcatEmbeddedContext 添加至 host 中,然后用 initializersToUse (initializersToUse 為實(shí)現(xiàn)了ServletContextInitializer接口 的集合,其中包括了getWebServer方法中的參數(shù) initializers )初始化 TomcatStarter ,并設(shè)置 TomcatEmbeddedContext 的Starter屬性為TomcatStarter。即TomcatEmbeddedContext的Starter屬性為TomcatStarter。在tomcat啟動(dòng)的時(shí)候會(huì)調(diào)用到TomcatStarter的onStartup方法。
8.getTomcatWebServer()中會(huì)創(chuàng)建TomcatwebServer類,并且傳入 tomcat 和 端口是否大于等于0 兩個(gè)參數(shù),代碼如下
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, getPort() >= 0); }
TomcatWebServer的構(gòu)造函數(shù)中,除了給tomcat和 autoStart賦值,還會(huì)調(diào)用初始化方法initialize,TomcatWebServer的構(gòu)造方法代碼如下,
public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; // 自動(dòng)開啟 this.autoStart = autoStart; // 初始化方法 initialize(); }
9.initialize()方法中會(huì)進(jìn)行TomcatWebServer的初始,關(guān)鍵代碼如下,
// Start the server to trigger initialization listeners // tomcat 服務(wù)器啟動(dòng) this.tomcat.start();
整個(gè)tomcat的啟動(dòng)比較復(fù)雜,有興趣的可以去研究下tomcat源碼,這里不做多講直接給出結(jié)論。其中會(huì)調(diào)用上面創(chuàng)建的TomcatStarter類的onStartup方法,并傳入tomcat的servletContext對(duì)象。TomcatStarter的onStartup具體代碼如下,
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException { try { // 遍歷已經(jīng)注冊(cè)的 initializers, 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()); } } }
onStartup方法中會(huì)遍歷 initializers 集合并且調(diào)用其onStartup()方法,而initializers包括了我們?cè)谡{(diào)用getWebServer時(shí)傳入的getSelfInitializer方法,該方法體的主要業(yè)務(wù)邏輯上面已經(jīng)講了(請(qǐng)看本章的第5點(diǎn)),就是給傳進(jìn)去的servletContext添加當(dāng)前Spring容器中注入的SpringMVC的DispatchServlet類。
9.最后tomcat完成初始化,會(huì)監(jiān)聽一個(gè)具體的端口。至此,tomcat啟動(dòng)的過程已經(jīng)把SpringMVC的DispatchServlet類添加到了tomcat的servletContext對(duì)象中。當(dāng)請(qǐng)求進(jìn)來Web服務(wù)器的時(shí)候會(huì)轉(zhuǎn)到DispatchServlet中。DispatchServlet的整個(gè)初始化過程這里不細(xì)講了,請(qǐng)參考 SpringMVC請(qǐng)求流程源碼分析 文章。
到此這篇關(guān)于SpringBoot中Tomcat和SpringMVC整合源碼分析的文章就介紹到這了,更多相關(guān)SpringBoot Tomcat和SpringMVC整合內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 關(guān)于springboot-starter-undertow和tomcat的區(qū)別說明
- SpringBoot?SPI?機(jī)制和實(shí)現(xiàn)自定義?starter
- springboot自定義starter方法及注解實(shí)例
- SpringBoot詳細(xì)分析自動(dòng)裝配原理并實(shí)現(xiàn)starter
- SpringBoot項(xiàng)目為何引入大量的starter?如何自定義starter?
- 教你利用SpringBoot寫一個(gè)屬于自己的Starter
- SpringBoot如何自定義starter
- SpringBoot配置和切換Tomcat流程詳解
- SpringBoot Starter機(jī)制及整合tomcat的實(shí)現(xiàn)詳解
相關(guān)文章
Java Fluent Mybatis 項(xiàng)目工程化與常規(guī)操作詳解流程篇 上
Java中常用的ORM框架主要是mybatis, hibernate, JPA等框架。國內(nèi)又以Mybatis用的多,基于mybatis上的增強(qiáng)框架,又有mybatis plus和TK mybatis等。今天我們介紹一個(gè)新的mybatis增強(qiáng)框架 fluent mybatis2021-10-10Java NegativeArraySizeException異常解決方案
這篇文章主要介紹了Java NegativeArraySizeException異常解決方案,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08淺談JAVA字符串匹配算法indexOf函數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了淺談字符串匹配算法indexOf函數(shù)的實(shí)現(xiàn)方法,indexOf函數(shù)我們可以查找一個(gè)字符串(模式串)是否在另一個(gè)字符串(主串)出現(xiàn)過。對(duì)此感興趣的可以來了解一下2020-07-07struts2簡介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
Struts2框架是MVC流程框架,適合分層開發(fā),這篇文章主要為大家詳細(xì)介紹了struts2簡介的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Java中BigDecimal除法使用不當(dāng)導(dǎo)致精度問題
本文主要介紹了Java中BigDecimal除法使用不當(dāng)導(dǎo)致精度問題,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11mybatis Example的Criteria用法:or與isNull詳解
這篇文章主要介紹了mybatis Example的Criteria用法:or與isNull詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12