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);
    }

注册ServletWebServerFactoryCustomizerTomcatServletWebServerFactoryCustomizer两个对象到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容器自动配置原理的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot源码学习系列之嵌入式Servlet容器

SpringBoot源码学习系列之嵌入式Servlet容器

嵌入式Servlet容器自动配置启动自定义配置原理

嵌入式Servlet容器自动配置启动自定义配置原理

SpringBoot -- 嵌入式Servlet容器

SpringBoot起飞系列-配置嵌入式Servlet容器