Spring boot整合tomcat底层原理剖析

本站所有内容来自互联网收集,仅供学习和交流,请勿用于商业用途。如有侵权、不妥之处,请第一时间联系我们删除!Q群:迪思分享

免费资源网 – https://freexyz.cn/
目录本文结论spring-boot-starter-web内部有什么?TomcatServletWebServerFactory的作用:获取WebServer对象spring boot启动的时候启动tomcat获取tomcat的配置ServletWebServerFactoryCustomizer这个Bean是哪里的?

从源码层面理解spring boot的默认web容器,以及他们是如何关联起来的。

本文结论

源码基于spring boot2.6.6项目的pom.xml中存在spring-boot-starter-web的时候,在项目启动时候就会自动启动一个Tomcat。自动配置类ServletWebServerFactoryAutoConfiguration找到系统中的所有web容器。我们以tomcat为主。构建TomcatServletWebServerFactory的bean。SpringBoot的启动过程中,会调用核心的refresh方法,内部会执行onRefresh()方法,onRefresh()方法是一个模板方法,他会执行会执行子类ServletWebServerApplicationContext的onRefresh()方法。onRefresh()方法中调用getWebServer启动web容器。

spring-boot-starter-web内部有什么?

在spring-boot-starter-web这个starter中,其实内部间接的引入了spring-boot-starter-tomcat这个starter,这个spring-boot-starter-tomcat又引入了tomcat-embed-core依赖,所以只要我们项目中依赖了spring-boot-starter-web就相当于依赖了Tomcat。

Spring boot整合tomcat底层原理剖析插图

Spring boot整合tomcat底层原理剖析插图1

自动配置类:ServletWebServerFactoryAutoConfiguration在spring-boot-autoconfigure-2.6.6.jar这个包中的spring.factories文件内,配置了大量的自动配置类,其中就包括自动配置tomcat的自动配置类:ServletWebServerFactoryAutoConfiguration

Spring boot整合tomcat底层原理剖析插图2

自动配置类的代码如下 // full模式 @Configuration(proxyBeanMethods = false) // 配置类解析顺序 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 条件注解:表示项目依赖中要有ServletRequest类(server api) @ConditionalOnClass(ServletRequest.class) // 表示项目应用类型得是SpringMVC(在启动过程中获取的SpringBoot应用类型) @ConditionalOnWebApplication(type = Type.SERVLET) // 读取server下的配置文件 @EnableConfigurationProperties(ServerProperties.class) // import具体的加载配置的类和具体web实现容器 @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { …… } ServletRequest是存在于tomcat-embed-core-9.0.60.jar中的的一个类,所以@ConditionalOnClass(ServletRequest.clas s)会满足。spring-boot-starter-web中,间接的引入了spring-web、spring-webmvc等依赖,所以@ConditionalOnWebApplication(type = Type.SERVLET)条件满足。上面的俩个条件都满足,所以spring回去解析这个配置类,在解析过程中会发现他import了三个类!我们重点关注EmbeddedTomcat。其他俩个的内部条件注解不满足! @Configuration(proxyBeanMethods = false) // tomcat内部的类,肯定都存在 @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) // 程序员如果自定义了ServletWebServerFactory的Bean,那么这个Bean就不加载。 @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(); // orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义。这个Bean可以设置一些tomcat的配置,比如端口、协议… // TomcatConnectorCustomizer:是用来配置Tomcat中的Connector组件的 factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); // TomcatContextCustomizer:是用来配置Tomcat中的Context组件的 factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); // TomcatProtocolHandlerCustomizer:是用来配置Tomcat中的ProtocolHandler组件的 factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } } 对于另外的EmbeddedJetty和EmbeddedUndertow,逻辑类似,都是判断项目依赖中是否有Jetty和Undertow的依赖,如果有,那么对应在Spring容器中就会存在JettyServletWebServerFactory类型的Bean、或者存在UndertowServletWebServerFactory类型的Bean。

TomcatServletWebServerFactory的作用:获取WebServer对象

TomcatServletWebServerFactory他实现了ServletWebServerFactory这个接口。ServletWebServerFactory接口内部只有一个方法是获取WebServer对象。

Spring boot整合tomcat底层原理剖析插图3

WebServer拥有启动、停止、获取端口等方法,就会发现WebServer其实指的就是Tomcat、Jetty、Undertow。

Spring boot整合tomcat底层原理剖析插图4

而TomcatServletWebServerFactory就是用来生成Tomcat所对应的WebServer对象,具体一点就是TomcatWebServer对象,并且在生成TomcatWebServer对象时会把Tomcat给启动起来。在源码中,调用TomcatServletWebServerFactory对象的getWebServer()方法时就会启动Tomcat。 public WebServer getWebServer(ServletContextInitializer… initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } // 构建tomcat对象 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); // 启动tomcat,这个方法内部有this.tomcat.start(); return getTomcatWebServer(tomcat); }

spring boot启动的时候启动tomcat

SpringBoot的启动过程中,会调用核心的refresh方法,内部会执行onRefresh()方法,onRefresh()方法是一个模板方法,他会执行会执行子类ServletWebServerApplicationContext的onRefresh()方法。 protected void onRefresh() { // 模板方法,先调用它父类的,一般是空方法 super.onRefresh(); try { // 创建web容器 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException(“Unable to start web server”, ex); } }

这个方法会调用createWebServer()方法。

// 最核心的俩行代码 private void createWebServer() { …… // 获取web容器,多个或者没有的时候报错 ServletWebServerFactory factory = getWebServerFactory(); // 调用这个容器的getWebServer方法,上面的启动tomcat的方法! this.webServer = factory.getWebServer(getSelfInitializer()); …… } getWebServerFactory控制项目组有且只能有一个web容器! protected ServletWebServerFactory getWebServerFactory() { // Use bean names so that we dont consider the hierarchy // 得到所有类型为ServletWebServerFactory的Bean。TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory都是他得到子类! 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)); } // 返回唯一的一个web容器! return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }

获取tomcat的配置

自动配置类ServletWebServerFactoryAutoConfiguration上除了import三个web容器,还import了BeanPostProcessorsRegistrar。BeanPostProcessorsRegistrar实现了ImportBeanDefinitionRegistrar,所以他会在spring启动的时候调用registerBeanDefinitions方法。registerBeanDefinitions会注册一个Bean:webServerFactoryCustomizerBeanPostProcessor。 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // Bean工厂,一个Aware回调进行赋值 if (this.beanFactory == null) { return; } // 注册webServerFactoryCustomizerBeanPostProcessor这个Bean。 registerSyntheticBeanIfMissing(registry, “webServerFactoryCustomizerBeanPostProcessor”, WebServerFactoryCustomizerBeanPostProcessor.class, WebServerFactoryCustomizerBeanPostProcessor::new); // 注册errorPageRegistrarBeanPostProcessor registerSyntheticBeanIfMissing(registry, “errorPageRegistrarBeanPostProcessor”, ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new); } webServerFactoryCustomizerBeanPostProcessor实现了BeanPostProcessor,所以他会在启动的时候调用postProcessBeforeInitialization方法。 private void postProcessBeforeInitialization(WebServerFactory webServerFactory) { // 找到WebServerFactoryCustomizer的Bean LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory) // 标记日志用的类 .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class) // 调用customize方法,传入webServerFactory .invoke((customizer) -> customizer.customize(webServerFactory)); } postProcessBeforeInitialization中会调用WebServerFactoryCustomizer类customize方法,在系统中的唯一实现:ServletWebServerFactoryCustomizer的customize方法。customize把配置中的内容设置到ConfigurableServletWebServerFactory对象中。他的实现TomcatServletWebServerFactory在启动的时候就会有值! @Override public void customize(ConfigurableServletWebServerFactory factory) { PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(this.serverProperties::getPort).to(factory::setPort); map.from(this.serverProperties::getAddress).to(factory::setAddress); map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath); map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName); map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet); map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession); map.from(this.serverProperties::getSsl).to(factory::setSsl); map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp); map.from(this.serverProperties::getCompression).to(factory::setCompression); map.from(this.serverProperties::getHttp2).to(factory::setHttp2); map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader); map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters); map.from(this.serverProperties.getShutdown()).to(factory::setShutdown); for (WebListenerRegistrar registrar : this.webListenerRegistrars) { registrar.register(factory); } if (!CollectionUtils.isEmpty(this.cookieSameSiteSuppliers)) { factory.setCookieSameSiteSuppliers(this.cookieSameSiteSuppliers); } }

ServletWebServerFactoryCustomizer这个Bean是哪里的?

在我们自动配置类ServletWebServerFactoryAutoConfiguration中定义。 @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())); }
免费资源网 – https://freexyz.cn/


© 版权声明
THE END
★喜欢这篇文章吗?喜欢的话,麻烦动动手指支持一下!★
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容