嵌入式Servlet容器自动配置原理
带着问题看源码
- 为什么可以根据配置的依赖自动使用对应的servlet容器?
- 怎么根据配置文件中的 server.xx 以及 WebServletFactoryCustomizer 去设置servlet容器?
- 嵌入式 servlet 容器是怎么启动的?
ServletWebServerFactoryAutoConfiguration 就是我们servlet容器自动配置类
@Configuration(proxyBeanMethods = false)
//自动配置顺序
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
//容器中必须有ServletRequest这个类,只要有使用 servlet容器都会有这个类,如果项目没有ServletRequest容器的话,那么这个类也不存在
@ConditionalOnClass(ServletRequest.class)
//类型必须是servlet
@ConditionalOnWebApplication(type = Type.SERVLET)
//配置类
@EnableConfigurationProperties(ServerProperties.class)
//导入类
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
//阿雷,这咋一看,这下面这三个类好像有点眼熟欸!我来抓一下点
//EmbeddedTomcat.class EmbeddedJetty.class EmbeddedUndertow.class
//Embedded 是嵌入式的意思,组合起来就是嵌入式容器的意思,啧啧啧,机智如我啊
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
//.....
}
-
为什么可以根据配置的依赖自动使用对应的servlet容器?
通过 @Import 导入 Embeddedxxx.class
//导入类 @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, //阿雷,这咋一看,这下面这三个类好像有点眼熟欸!我来抓一下点 //EmbeddedTomcat.class EmbeddedJetty.class EmbeddedUndertow.class //Embedded 是嵌入式的意思,组合起来就是嵌入式容器的意思,啧啧啧,机智如你啊 ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { //..... }
每个 Embeddedxxx.class 中都配置了相应的 @ConditionalOnClass,会根据当前的servlet容器
//=====================tomcat @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedTomcat {} //=====================jetty @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedJetty {} //=====================undertow @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedUndertow {}
如果没有对应的tomcat场景启动器,该注解就不会生效
-
怎么根据配置文件中的 server.xxx 以及 WebServletFactoryCustomizer 去设置servlet容器?
- ServletWebServerFactoryCustomizer 也实现了 WebServerFactoryCustomizer ,说明它也是定制 servlet 容器的
- servlet 容器配置文件通用定制器
@Bean public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer(serverProperties); }
根据配置文件中的server.xxx来进行定制servlet容器
//ServletWebServerFactiryCustomizer.class //既然继承了 WebServerFactoryCustomizer,那么他也同样的重写了customize方法 @Override public void customize(ConfigurableServletWebServerFactory factory) { PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); //这里这边涉及到jdk8的语法新特性,简单介绍一下下面这句代码的意思 //获得this.serverProperties配置中的端口,设置端口 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); }
往下看,发现 …
有一个叫做 TomcatServletWebServerFactoryCustomizer 的 bean 方法也实现继承了 WebServerFactoryCustomizer!
@Bean @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); }
TomcatServletWebServerFactoryCustomizer 这个类就是一个 tomcat配置文件定制器
- 根据配置文件中server.tomcat.xx定制servler容器
//TomcatServletWebServerFactoryCustomizer.class //既然继承了 WebServerFactoryCustomizer,那么他也同样的重写了customize方法 //欸嘿,这里的设置语法跟上面那块有点不一样,但这不是重点,哈哈 //这里也与上面同理:根据配置文件中server.tomcat.xxx 定制servlet容器 @Override public void customize(TomcatServletWebServerFactory factory) { //获取配置中关于tomcat的配置 ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat(); if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) { factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns()); } if (tomcatProperties.getRedirectContextRoot() != null) { customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot()); } customizeUseRelativeRedirects(factory, tomcatProperties.isUseRelativeRedirects()); factory.setDisableMBeanRegistry(!tomcatProperties.getMbeanregistry().isEnabled()); }
怎么让所有的 WebServerFactoryCustomizerBean 一一调用的
-
BeanPostProcessorsRegistrar
先看看源码对这个类的解释:
注册一个WebServerFactoryCustomizerBeanPostProcessor 。 通过ImportBeanDefinitionRegistrar注册ImportBeanDefinitionRegistrar进行早期注册
实现 ImportBeanDefinitionRegistrar 会提供一个方法,并且提供 BeanDefinitonRegistrar 让我们去注册 bean
//BeanPostProcessorsRegistrar.class @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory == null) { return; } registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); }
注册了 WebServerFactoryCustormizerBeanPostProcessor
//BeanPostProcessor 在bean创建时,在初始化的时候就会调用 postProcessBeforeInitialization方法(初始化前) postProcessAfterInitialization方法(初始化后) public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
//WebServerFactoryCustomizerBeanPostProcessor.class @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //判断当前创建的bean是不是webServerFactory if (bean instanceof WebServerFactory) { //然后调用postProcessBeforeInitialization方法 postProcessBeforeInitialization((WebServerFactory) bean); } return bean; }
当对应Embeddedxxx 启动时,就会在里面配置 WebFactory 一个WebServerFactory 类型的一个Bean,负责创建对应的容器和启动
我们看看它的类结构图
1、调用getCustomizers()
2、getWebServerFactoryCustomizerBeans() 就获取了所有实现了 WebServerFactoryCustomizer 接口的bean
获取 自定义的,和 ServletWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer
3、在 invoke 方法中循环调用所有实现了 WebServerFactoryCustomizer接口的Bean的customize方法进行一一定制
@SuppressWarnings("unchecked") private void postProcessBeforeInitialization(WebServerFactory webServerFactory) { //1、调用getCustomizers() LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory) //3、在 invoke 方法中循环调用所有实现了 WebServerFactoryCustomizer接口的Bean的customize方法进行一一定制 .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class) .invoke((customizer) -> customizer.customize(webServerFactory)); } private Collection<WebServerFactoryCustomizer<?>> getCustomizers() { if (this.customizers == null) { // Look up does not include the parent context //2、getWebServerFactoryCustomizerBeans() 就获取了所有实现了 WebServerFactoryCustomizer 接口的bean 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() { //beanFactory代表spring容器 return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values(); }
-
嵌入式 servlet 容器是怎么启动的?
TomcatServletWebServerFactory
- 自动配置中根据不同的以来,启动对应的一个Embeddedxxx,然后配置一个对应的 servlet 容器工厂类,比如 TomcatServletWebServerFactory
- 在我们启动 springboot 应用启动的时候,就会调用refresh方法,在这个方法里面又会调用onRefresh方法,然后调用getWebServer方法,创建 servlet 并启动
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } Tomcat tomcat = new Tomcat();//创建一个tomcat容器 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); 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); }
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { //进入这个构造器 return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown()); }
//创建一个新的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(); }
这块代码东西有点多,直接上截图看重点: