SpringBoot如何實(shí)現(xiàn)Tomcat自動(dòng)配置
目錄
準(zhǔn)備工作
我們知道SpringBoot的自動(dòng)裝配的秘密在 org.springframework.boot.autoconfigure
包下的 spring.factories
文件中,而嵌入Tomcat的原理就在這個(gè)文件中加載的一個(gè)配置類(lèi): org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
@Configuration @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 public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer(serverProperties); } @Bean @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); } /** * 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; } 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); } } } }
首先看一下上方的幾個(gè)注解
- @AutoConfigureOrder 這個(gè)注解是決定配置類(lèi)的加載順序的,當(dāng)注解里的值越小越先加載,而 Ordered.HIGHEST_PRECEDENCE 的值是 Integer.MIN_VALUE 也就是說(shuō)這個(gè)類(lèi)肯定是最先加載的那一批
- @ConditionalOnXXX 在之前的文章中已經(jīng)無(wú)數(shù)次提到了,就不再闡述了
- @EnableConfigurationProperties 開(kāi)啟 ServerProperties 類(lèi)的屬性值配置。而這個(gè)類(lèi)里面包含的就是Web服務(wù)的配置
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { private Integer port; private InetAddress address; @NestedConfigurationProperty private final ErrorProperties error = new ErrorProperties(); private Boolean useForwardHeaders; private String serverHeader; private int maxHttpHeaderSize = 0; // bytes private Duration connectionTimeout; @NestedConfigurationProperty private Ssl ssl; @NestedConfigurationProperty private final Compression compression = new Compression(); @NestedConfigurationProperty private final Http2 http2 = new Http2(); private final Servlet servlet = new Servlet(); private final Tomcat tomcat = new Tomcat(); private final Jetty jetty = new Jetty(); private final Undertow undertow = new Undertow(); }
這個(gè)類(lèi)的代碼太多了,這里就不一一貼出來(lái)了,我們平常在 application.properties
中配置的server.xxx就是這個(gè)類(lèi)中屬性
@Import BeanPostProcessorsRegistrar 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); } } }
這個(gè)類(lèi)注冊(cè)了兩個(gè)bean: WebServerFactoryCustomizerBeanPostProcessor
和 ErrorPageRegistrarBeanPostProcessor
關(guān)于這兩個(gè)bean的作用稍后再詳細(xì)介紹
- EmbeddedTomcat
@Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() { return new TomcatServletWebServerFactory(); } }
這個(gè)類(lèi)會(huì)在存在Tomcat相關(guān)jar包時(shí)添加一個(gè) TomcatServletWebServerFactory bean
其他兩個(gè)相信大家都知道怎么回事了
除了這些這個(gè)類(lèi)還注入了兩個(gè)類(lèi) ServletWebServerFactoryCustomizer
和 TomcatServletWebServerFactoryCustomizer
現(xiàn)在前期準(zhǔn)備工作已經(jīng)做好了,看一下這個(gè)Tomcat是如何啟動(dòng)的吧
啟動(dòng)
啟動(dòng)入口在 ServletWebServerApplicationContext 中的 onRefresh 方法
protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
Tomcat的啟動(dòng)就在 createWebServer
方法里面了
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); //第一次訪(fǎng)問(wèn)的時(shí)候兩個(gè)對(duì)象都為空 if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); 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(); }
首先看一下 getWebServerFactory
protected ServletWebServerFactory getWebServerFactory() { // 這里獲取的beanname就是上方注冊(cè)的tomcatServletWebServerFactory了 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); }
準(zhǔn)備環(huán)境里注冊(cè)的bean現(xiàn)在出來(lái)一個(gè)了。注意,上方還注冊(cè)了一個(gè)后置處理器 EmbeddedServletContainerCustomizerBeanPostProcessor
,獲取bean tomcatServletWebServerFactory
的時(shí)候就會(huì)執(zhí)行后置處理器的 postProcessBeforeInitialization
方法
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebServerFactory) { postProcessBeforeInitialization((WebServerFactory) bean); } return bean; } 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(); }
這個(gè)處理器的作用是獲得所有定制器,然后執(zhí)行定制器的方法
接著往下看
這個(gè)時(shí)候就可以啟動(dòng)Tomcat了
public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null ? this.baseDirectory : createTempDir("tomcat")); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); 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); } protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, getPort() >= 0); } public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; initialize(); } private void initialize() throws WebServerException { TomcatWebServer.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) { throw new WebServerException("Unable to start embedded Tomcat", ex); } } }
以上就是SpringBoot如何實(shí)現(xiàn)Tomcat自動(dòng)配置的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot實(shí)現(xiàn)Tomcat自動(dòng)配置的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java運(yùn)算符的知識(shí)點(diǎn)與代碼匯總
這篇文章主要給大家總結(jié)介紹了關(guān)于Java運(yùn)算符知識(shí)點(diǎn)與代碼的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04SpringBoot?AnnotationUtils工具類(lèi)的使用實(shí)例詳解
這篇文章主要介紹了SpringBoot?AnnotationUtils工具類(lèi)的使用,使用自定義注解標(biāo)記業(yè)務(wù)方法,原生Java獲取注解及AnnotationUtils工具類(lèi)獲取方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09SpringBoot中實(shí)現(xiàn)異步調(diào)用@Async詳解
這篇文章主要介紹了SpringBoot中實(shí)現(xiàn)異步調(diào)用@Async詳解,在SpringBoot的日常開(kāi)發(fā)中,一般都是同步調(diào)用的,但實(shí)際中有很多場(chǎng)景非常適合使用異步來(lái)處理,需要的朋友可以參考下2024-01-01Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解
這篇文章主要介紹了Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解,spring?boot拋棄了傳統(tǒng)xml配置文件,通過(guò)配置類(lèi)(標(biāo)注@Configuration的類(lèi),@Configuration配置類(lèi)相當(dāng)于一個(gè)xml配置文件)以JavaBean形式進(jìn)行相關(guān)配置,需要的朋友可以參考下2023-09-09struts升級(jí)到2.5.2遇到的問(wèn)題及解決方案(推薦)
原來(lái)的版本是2.3.x,由于安全原因需要升級(jí)到2.5.2。但是在升級(jí)過(guò)程中遇到各種各樣的問(wèn)題,下面小編給大家?guī)?lái)了struts升級(jí)到2.5.2遇到的問(wèn)題及解決方案,需要的朋友參考下吧2016-11-11關(guān)于java自定義線(xiàn)程池的原理與實(shí)現(xiàn)
本文介紹了如何自定義線(xiàn)程池和阻塞隊(duì)列,包括阻塞隊(duì)列的實(shí)現(xiàn)方法,線(xiàn)程池的構(gòu)建以及拒絕策略的應(yīng)用,詳細(xì)闡述了線(xiàn)程池中任務(wù)的提交和執(zhí)行流程,以及如何處理任務(wù)超出隊(duì)列容量的情況2022-04-04Springboot+MyBatist實(shí)現(xiàn)前后臺(tái)交互登陸功能方式
這篇文章主要介紹了Springboot+MyBatist實(shí)現(xiàn)前后臺(tái)交互登陸功能方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01javaweb實(shí)現(xiàn)投票系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了javaweb實(shí)現(xiàn)投票系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09SpringBoot feign動(dòng)態(tài)設(shè)置數(shù)據(jù)源(https請(qǐng)求)
這篇文章主要介紹了SpringBoot如何在運(yùn)行時(shí)feign動(dòng)態(tài)添加數(shù)據(jù)源,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-08-08