zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    WCF系列(一)BasicHttpBinding 和 WsHttpBinding 的不同点
    SQL Server中的事务与锁
    领域驱动设计之领域模型
    http请求到响应经历的阶段
    vs调试
    c# 基本值类型及其默认值
    ASP.NET中JSONP的两种实现以及其他跨域解决方案的简单实现
    通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core?
    最完整的数据倾斜解决方案(spark)
    Spark性能调优
  • 原文地址:https://www.cnblogs.com/wxw7blog/p/13270152.html
Copyright © 2011-2022 走看看