zoukankan      html  css  js  c++  java
  • 4_7.springboot2.x嵌入式servlet容器自动配置原理

    概述

    Spring Boot对所支持的Servlet Web服务器实现做了建模抽象:

    Servlet容器类型  WebServer模型接口 WebServer工厂实现类
    Tomcat    TomcatWebServer     TomcatServletWebServerFactory
    Jetty    JettyWebServer    JettyServletWebServerFactory
    Undertow  UndertowWebServer  UndertowServletWebServerFactory

    基于此模型概念,在一个Servlet Web应用中,Spring Boot会使用上表中所说的WebServer工厂组件生成相应的WebServer实例。而这里的WebServer工厂组件又是从哪里来的呢 ? 这就是自动配置类ServletWebServerFactoryAutoConfiguration的任务了。

    自动配置类ServletWebServerFactoryAutoConfiguration首先通过注解声明自己的生效条件:类 ServletRequest 存在于 classpath 上时才生效,也就是要求javax.servlet-api包必须被引用;当前应用必须是Spring MVC应用才生效;在以上条件被满足时,ServletWebServerFactoryAutoConfiguration引入了如下三个配置类 :

    EmbeddedTomcat

    EmbeddedJetty

    EmbeddedUndertow

    这三个配置类是ServletWebServerFactoryConfiguration的嵌套配置类,它们会分别检测classpath上存在的类,从而判断当前应用使用的是Tomcat/Jetty/Undertow中的哪一个Servlet Web服务器,从而决定定义相应的工厂组件bean : TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory。

    详解:

    ServletWebServerFactoryAutoConfiguration

    /**
    * EnableAutoConfiguration Auto-configuration for servlet web servers.
    *
    */
    @Configuration
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    // 仅在类 ServletRequest 存在于 classpath 上时才生效
    @ConditionalOnClass(ServletRequest.class)
    // 仅在当前应用是 Servlet Web 应用时才生效
    @ConditionalOnWebApplication(type = Type.SERVLET)
    // 确保前缀为 server 的配置参数加载到 bean ServerProperties
    @EnableConfigurationProperties(ServerProperties.class)
    // 1. 导入 ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar 以注册
    // BeanPostProcessor : WebServerFactoryCustomizerBeanPostProcessor 和
    // ErrorPageRegistrarBeanPostProcessor
    // 2. 导入 EmbeddedTomcat/EmbeddedJetty/EmbeddedUndertow 这三个属于
    // ServletWebServerFactoryConfiguration 的嵌套配置类,这三个配置类会分别检测
    // classpath上存在的类,从而判断当前应用使用的是 Tomcat/Jetty/Undertow,
    // 从而决定定义哪一个 Servlet Web服务器的工厂 bean :
    // TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory
    @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
            ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
            ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
            ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
    public class ServletWebServerFactoryAutoConfiguration {
    
    
        // 定义 bean ServletWebServerFactoryCustomizer,这里与properties中serverProperties配置联系,解释了为什么配置文件也可修改servlet容器配置
        @Bean
        public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
                ServerProperties serverProperties) {
            return new ServletWebServerFactoryCustomizer(serverProperties);
        }
    
    
        // 针对当前Servlet容器是Tomcat时定义该 bean,用于定制化 TomcatServletWebServerFactory
        @Bean
        // 仅在类 org.apache.catalina.startup.Tomcat 存在于 classpath 上时才生效
        @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
        public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
                ServerProperties serverProperties) {
            return new TomcatServletWebServerFactoryCustomizer(serverProperties);
        }
    
    
        /**
         * Registers a WebServerFactoryCustomizerBeanPostProcessor. Registered via
         * ImportBeanDefinitionRegistrar for early registration.
         * 这是一个 ImportBeanDefinitionRegistrar, 它会向容器注入两个 BeanPostProcessor :
         * 1. WebServerFactoryCustomizerBeanPostProcessor
         * 该 BeanPostProcessor 会搜集容器中所有的 WebServerFactoryCustomizer,对当前应用所采用的
         * WebServerFactory 被初始化前进行定制
         * 2. ErrorPageRegistrarBeanPostProcessor
         * 该 BeanPostProcessor 会搜集容器中所有的 ErrorPageRegistrar,添加到当前应用所采用的
         * ErrorPageRegistry 中,实际上,这里的 ErrorPageRegistry 会是 ConfigurableWebServerFactory,
         * 具体实现上来讲,是一个 ConfigurableTomcatWebServerFactory,ConfigurableJettyWebServerFactory
         * 或者 ConfigurableUndertowWebServerFactory,分别对应 Tomcat, Jetty, Undertow 这三种
         * Servlet Web 容器的工厂类
         */
        public static class BeanPostProcessorsRegistrar
                implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    
    
            private ConfigurableListableBeanFactory beanFactory;
    
    
            @Override
            public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
                if (beanFactory instanceof ConfigurableListableBeanFactory) {
                    this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
                }
            }
    
    
            @Override
            public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                                BeanDefinitionRegistry registry) {
                if (this.beanFactory == null) {
                    return;
                }
                registerSyntheticBeanIfMissing(registry,
                        "webServerFactoryCustomizerBeanPostProcessor",
                        WebServerFactoryCustomizerBeanPostProcessor.class);
                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);
                }
            }
    
    
        }

    ServletWebServerFactoryConfiguration

           ServletWebServerFactoryConfiguration是一个针对ServletWebServerFactory进行配置的配置类。它通过检测应用classpath存在的类,从而判断当前应用要使用哪个Servlet容器:Tomcat,Jetty还是Undertow。检测出来之后,定义相应的Servlet Web服务器工厂组件bean 

    Servlet容器类型  WebServerFactory实现类
    Tomcat  TomcatServletWebServerFactory
    Jetty     JettyServletWebServerFactory
    Undertow     UndertowServletWebServerFactory

    注意,以上三个实现类都继承自抽象基类AbstractServletWebServerFactory,实现了接口WebServerFactory,ErrorPageRegistry。该特征会被WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor使用,用于对WebServerFactory进行定制化,以及设置相应的错误页面。

    @Configuration
    class ServletWebServerFactoryConfiguration {
    
    @Configuration
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {
    
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
    return new TomcatServletWebServerFactory();
    }
    
    }
    
    /**
    * Nested configuration if Jetty is being used.
    */
    @Configuration
    @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedJetty {
    
    @Bean
    public JettyServletWebServerFactory JettyServletWebServerFactory() {
    return new JettyServletWebServerFactory();
    }
    
    }
    
    /**
    * Nested configuration if Undertow is being used.
    */
    @Configuration
    @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedUndertow {
    
    @Bean
    public UndertowServletWebServerFactory undertowServletWebServerFactory() {
    return new UndertowServletWebServerFactory();
    }
    
    }
    
    }

    查看代码:

    这里以TomcatServletWebServerFactory:

    注意这里此方法返回值:getTomcatWebServer(tomcat);查看此方法:传入两个参数,只要得到端口号大于0就默认启动

    同时查看:其中TomcatWebServer类构造方法:

    这时候tomcat启动开始。查看initialize();方法

    private void initialize() throws WebServerException {
    		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    		synchronized (this.monitor) {
    			try {
    				addInstanceIdToEngineName();
    
    				Context context = findContext();
    				context.addLifecycleListener((event) -> {
    					if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
    						// Remove service connectors so that protocol binding doesn't
    						// happen when the service is started.
    						removeServiceConnectors();
    					}
    				});
    
    				// Start the server to trigger initialization listeners
    				this.tomcat.start();
    
    				// We can re-throw failure exception directly in the main thread
    				rethrowDeferredStartupExceptions();
    
    				try {
    					ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
    				}
    				catch (NamingException ex) {
    					// Naming is not enabled. Continue
    				}
    
    				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
    				// blocking non-daemon to stop immediate shutdown
    				startDaemonAwaitThread();
    			}
    			catch (Exception ex) {
    				stopSilently();
    				destroySilently();
    				throw new WebServerException("Unable to start embedded Tomcat", ex);
    			}
    		}
    	}

    可以看到有启动: this.tomcat.start();启动!

    思考

    如何对嵌入式容器的配置修改是怎么生效的

    两种方法:1、修改properties 

    2、配置servlet容器定制器

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> myWebServerFactoryCustomizer() {
        return new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
            public void customize(TomcatServletWebServerFactory factory) {
                //设置相关配置
                factory.setPort(8080);
            }
        };
    }

    思考:怎么修改的原理?

    springboot版本对照:

    1.5.x:-->

    //导入后置处理器的注册器,作用给容器导入组件

    @Import(BeanPostProcessorsRegistrar.class

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    @ConditionalOnWebApplication
    //导入后置处理器的注册器
    @Import(BeanPostProcessorsRegistrar.class)
    public class EmbeddedServletContainerAutoConfiguration {.....}

    2.x:将其作为内部类导入组件

    @Configuration
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @ConditionalOnClass(ServletRequest.class)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @EnableConfigurationProperties(ServerProperties.class)
    @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
          ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
          ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
          ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
    public class ServletWebServerFactoryAutoConfiguration {
        .....
        public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
        ...
    
            @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
           if (this.beanFactory == null) {
          return;
               }
            //导入servlet定制器的后置处理器    
           registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
           WebServerFactoryCustomizerBeanPostProcessor.class);
           registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
             ErrorPageRegistrarBeanPostProcessor.class);
                }
         }
    
    
    }

    观察方法:BeanPostProcessorsRegistrar,导入servlet定制器的后置处理器 

    作用:后置处理器:bean初始化前后(创建完对象,还没赋值赋值)执行初始化工作

    WebServerFactoryCustomizerBeanPostProcessor

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
       if (bean instanceof WebServerFactory) {
          postProcessBeforeInitialization((WebServerFactory) bean);
       }
       return bean;
    }

    初始化之前:判断如果当前初始化组件是WebServerFactory 的一个组件,就调用postProcessBeforeInitialization此方法,

    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
    LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
    .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
    .invoke((customizer) -> customizer.customize(webServerFactory));
    }

    此方法:获取所有的定制器,调用每一个定制器的customize方法,来给servlet容器进行属性赋值

    getCustomizers()方法以上调用:

    
    private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
    if (this.customizers == null) {
    // Look up does not include the parent context
    this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
    this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
    this.customizers = Collections.unmodifiableList(this.customizers);
    }
    return this.customizers;
    }
    
    this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());此方法点进去:
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
    return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
    }

    作用:从容器中获取所有该类型的组件:WebServerFactoryCustomizer这里该组件我们可以定制。

    思考2?在properties中是怎样定制servlet容器的??

    @Configuration
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @ConditionalOnClass(ServletRequest.class)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @EnableConfigurationProperties(ServerProperties.class)
    @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
          ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
          ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
          ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
    public class ServletWebServerFactoryAutoConfiguration {......}

    2.x:中直接在servelt容器工厂的自动配置类:@EnableConfigurationProperties(ServerProperties.class)注解,向容器中添加ServerProperties组件,在SpringBoot中xxxProperties类都是和配置文件映射的类,用户可以通过配置文件配置类中属性。例如:用户可以通过server.port配置容器端口号。

    1.5.x:serverProperties中实现方法。

    嵌入式servlet容器自动配置原理的步骤

    1)、SpringBoot根据导入的依赖情况,容器中导入的ServletWebServerFactoryConfiguration配置类,给容器添加相应的TomcatServletWebServerFactory,通过TomcatServletWebServerFactory的getWebServer()启动servelt容器

    2)、容器中某个组件要创建对象就会惊动后置处理器;@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,

    只要是嵌入式的Servlet容器工厂,后置处理器就工作,用与定制嵌入式容器的配置修改,

    3)、后置处理器,执行postProcessBeforeInitialization()方法,调用定制器的定制方法;

  • 相关阅读:
    Tomcat安装和使用
    mysql5.7.18安装配置
    Memcached安装与使用
    Redis
    nginx的安装与使用
    python操作mysql
    Paramiko模块
    协程与异步IO
    Queue与生产者消费者模型
    C# 生成验证码 方法二
  • 原文地址:https://www.cnblogs.com/jatpeo/p/11767502.html
Copyright © 2011-2022 走看看