• SpringBoot2.3.1嵌入式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

  • 相关阅读:
    tensorflow 2.0 学习 (十) 拟合与过拟合问题
    tensorflow 2.0 学习 (九) tensorboard可视化功能认识
    tensorflow 2.0 学习 (八) keras模块的认识
    tensorflow 2.0 学习 (七) 反向传播代码逐步实现
    tensorflow 2.0 学习 (六) Himmelblua函数求极值
    tensorflow 2.0 学习 (五)MPG全连接网络训练与测试
    arp协议简单介绍
    Pthread spinlock自旋锁
    线程和进程状态
    内核态(内核空间)和用户态(用户空间)的区别和联系·
  • 原文地址:https://www.cnblogs.com/wxw7blog/p/13270152.html
Copyright © 2020-2023  润新知