SpringBoot嵌入式Servlet容器自动配置原理
Posted 竹马今安在
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot嵌入式Servlet容器自动配置原理相关的知识,希望对你有一定的参考价值。
Servlet容器的自动配置是通过ServletWebServerFactoryAutoConfiguration 自动配置类来实现的,先不看里面方法,从类注解看起。
@Configuration( proxyBeanMethods = false ) //表示这个一个配置类 @AutoConfigureOrder(-2147483648) @ConditionalOnClass({ServletRequest.class}) //只有包含ServletRequest这个类的时候此配置类才会生效 @ConditionalOnWebApplication( type = Type.SERVLET )//当前是web环境 @EnableConfigurationProperties({ServerProperties.class})//启动指定类的ConfigurationProperties功能;将配置文件中对应的值和ServerProperties绑定起来;并把ServerProperties加入到ioc容器中 @Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})//导入这四个Bean到容器中,下面详细讲解 public class ServletWebServerFactoryAutoConfiguration { }
首先略过BeanPostProcessorsRegistrar,来看EmbeddeTomcat.class
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})//只有包含Tomcat类时才会生效,不同的Servlet容器引入jar包不同,Jetty与UnderTow的配置也是通过这个区分 @ConditionalOnMissingBean( value = {ServletWebServerFactory.class}, search = SearchStrategy.CURRENT )//当系统中没有ServletWebServerFactory时,才会使用下面创建的、//ServletWebServerFactory,通过这其实可以看出,我们可以自定义ServletWebServerFactory来实现自己的配置
static class EmbeddedTomcat {
EmbeddedTomcat() {
}
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
其实只干了一件事,就是将TomcatServletWebServerFactory 注册到容器当中,点开TomcatServletWebServerFactory 看一下。
里面关键是这个方法
public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } Tomcat tomcat = new Tomcat(); File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); Iterator var5 = this.additionalTomcatConnectors.iterator(); while(var5.hasNext()) { Connector additionalConnector = (Connector)var5.next(); tomcat.getService().addConnector(additionalConnector); } this.prepareContext(tomcat.getHost(), initializers); return this.getTomcatWebServer(tomcat); }
其实用用java生成了一个tomcat,然后继续点击getTomcatWebServer,TomcatServletWebServerFactory 会新创建类一个TomcatWebServer
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown()); }
在不修改端口号的情况下TomcatServletWebServerFactory 默认配置端口为8080
public int getPort() { return this.port; } private int port = 8080;
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) { this.monitor = new Object(); this.serviceConnectors = new HashMap(); Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null; this.initialize(); //执行初始化 } private void initialize() throws WebServerException { logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false)); synchronized(this.monitor) { try { this.addInstanceIdToEngineName(); Context context = this.findContext(); context.addLifecycleListener((event) -> { if (context.equals(event.getSource()) && "start".equals(event.getType())) { this.removeServiceConnectors(); } }); this.tomcat.start();//tomcat被启动 this.rethrowDeferredStartupExceptions(); try { ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader()); } catch (NamingException var5) { } this.startDaemonAwaitThread(); } catch (Exception var6) { this.stopSilently(); this.destroySilently(); throw new WebServerException("Unable to start embedded Tomcat", var6); } } }
同理EmbeddedJetty与EmbeddedUndertow也是如此。
那么我们如何自定义相关配置,又如何生效呢?
有两种方式:
1、通过application.yml里面配置。如何生效?
回到最上面,引入ServerProperties。打开看一下
@ConfigurationProperties( prefix = "server", ignoreUnknownFields = true )//是与配置文件中的server下的内容绑定,我们能配置的内容也就是下面下面这些 public class ServerProperties { private Integer port; private InetAddress address; @NestedConfigurationProperty private final ErrorProperties error = new ErrorProperties(); private ServerProperties.ForwardHeadersStrategy forwardHeadersStrategy; private String serverHeader; private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L); private Shutdown shutdown; @NestedConfigurationProperty private Ssl ssl; @NestedConfigurationProperty private final Compression compression; @NestedConfigurationProperty private final Http2 http2; private final ServerProperties.Servlet servlet; private final ServerProperties.Tomcat tomcat; private final ServerProperties.Jetty jetty; private final ServerProperties.Netty netty; private final ServerProperties.Undertow undertow; }
配置内容与bean绑定以后如何生效的呢?下面就用到了自动配置类里面的代码
@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); }
注册ServletWebServerFactoryCustomizer与TomcatServletWebServerFactoryCustomizer两个对象到Spring容器中时传入。
public class ServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered { private final ServerProperties serverProperties; public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) { this.serverProperties = serverProperties; } public int getOrder() { return 0; } public void customize(ConfigurableServletWebServerFactory factory) { PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); ServerProperties var10001 = this.serverProperties; var10001.getClass(); map.from(var10001::getPort).to(factory::setPort); var10001 = this.serverProperties; var10001.getClass(); map.from(var10001::getAddress).to(factory::setAddress); Servlet var3 = this.serverProperties.getServlet(); var3.getClass(); map.from(var3::getContextPath).to(factory::setContextPath); var3 = this.serverProperties.getServlet(); var3.getClass(); map.from(var3::getApplicationDisplayName).to(factory::setDisplayName); var3 = this.serverProperties.getServlet(); var3.getClass(); map.from(var3::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet); var3 = this.serverProperties.getServlet(); var3.getClass(); map.from(var3::getSession).to(factory::setSession); var10001 = this.serverProperties; var10001.getClass(); map.from(var10001::getSsl).to(factory::setSsl); var3 = this.serverProperties.getServlet(); var3.getClass(); map.from(var3::getJsp).to(factory::setJsp); var10001 = this.serverProperties; var10001.getClass(); map.from(var10001::getCompression).to(factory::setCompression); var10001 = this.serverProperties; var10001.getClass(); map.from(var10001::getHttp2).to(factory::setHttp2); var10001 = this.serverProperties; var10001.getClass(); map.from(var10001::getServerHeader).to(factory::setServerHeader); var3 = this.serverProperties.getServlet(); var3.getClass(); map.from(var3::getContextParameters).to(factory::setInitParameters); map.from(this.serverProperties.getShutdown()).to(factory::setShutdown); } }
public class TomcatServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered { private final ServerProperties serverProperties; public TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) { this.serverProperties = serverProperties; } public int getOrder() { return 0; } public void customize(TomcatServletWebServerFactory factory) { Tomcat tomcatProperties = this.serverProperties.getTomcat(); if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) { factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns()); } if (tomcatProperties.getRedirectContextRoot() != null) { this.customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot()); } this.customizeUseRelativeRedirects(factory, tomcatProperties.isUseRelativeRedirects()); factory.setDisableMBeanRegistry(!tomcatProperties.getMbeanregistry().isEnabled()); } private void customizeRedirectContextRoot(ConfigurableTomcatWebServerFactory factory, boolean redirectContextRoot) { factory.addContextCustomizers(new TomcatContextCustomizer[]{(context) -> { context.setMapperContextRootRedirectEnabled(redirectContextRoot); }}); } private void customizeUseRelativeRedirects(ConfigurableTomcatWebServerFactory factory, boolean useRelativeRedirects) { factory.addContextCustomizers(new TomcatContextCustomizer[]{(context) -> { context.setUseRelativeRedirects(useRelativeRedirects); }}); } }
打开两个类可以看到在customize方法里面会被应用,去配置对应的容器工厂类。联系上面,工厂类是用来注册容器实例的,那么这个customize这个配置一定会在此之前被调用,什么时候呢?这时候来看开头忽略的Import
BeanPostProcessorsRegistrar
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; public BeanPostProcessorsRegistrar() { } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof ConfigurableListableBeanFactory) { this.beanFactory = (ConfigurableListableBeanFactory)beanFactory; } } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory != null) { this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); this.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); } } }
this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
这一行是关键,打开
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { private ListableBeanFactory beanFactory; private List<WebServerFactoryCustomizer<?>> customizers; public WebServerFactoryCustomizerBeanPostProcessor() { } public void setBeanFactory(BeanFactory beanFactory) { Assert.isInstanceOf(ListableBeanFactory.class, beanFactory, "WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory"); this.beanFactory = (ListableBeanFactory)beanFactory; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebServerFactory) { this.postProcessBeforeInitialization((WebServerFactory)bean); } return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } //在Servlet初始化之前执行 private void postProcessBeforeInitialization(WebServerFactory webServerFactory) { ((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, //取出所有Customizers this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
//对Spring容器中的WebServerFactory执行customize方法。 customizer.customize(webServerFactory); }); } private Collection<WebServerFactoryCustomizer<?>> getCustomizers() { if (this.customizers == null) { this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans()); this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE); this.customizers = Collections.unmodifiableList(this.customizers); } return this.customizers; } private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() { return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values(); //取出Spring容器中Customizer的类型是继承自WebServerFactoryCustomizer } }
通过
customizer.customize(webServerFactory);
我们在application里面配置的内容就应用到了其中。
另一种是就是自定义的WebServerFactory,可以
@Configuration public class WebConfigurer { @Bean public ConfigurableServletWebServerFactory configurableServletWebServerFactory(){ TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.setPort(8585); return factory; } }
将我们自定义的ConfigurableServletWebServerFactory注入到了Spring容器中
@ConditionalOnMissingBean( value = {ServletWebServerFactory.class},
这样自定义的Factory就不会生效了,而自定义的Factory注入到Spring容器中以后会被
customizer.customize(webServerFactory);
应用,这么看执行过程,如果同时配置了Factory Bean与yml,应该Factory Bean里面配置的会被yml里面的同等配置替代,所以yml配置优先。
有一个疑问,我只找到了
@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); }
并没有找到像
Jetty和Undertow的Customizer的注册Bean
以上是关于SpringBoot嵌入式Servlet容器自动配置原理的主要内容,如果未能解决你的问题,请参考以下文章