zoukankan      html  css  js  c++  java
  • springboot嵌入式servlet容器的自动配置以及原理

    配置嵌入式Servlet容器

    SpringBoot默认用的是Tomcat作为嵌入式的Servlet容器

    如何定制和修改servlet容器的相关配置

    • 修改和server有关的配置(ServerProperties)

          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;
      
      server.port=8080
      
      //通用的Servler容器设置
      server.xxx
      //Tomcat的设置
      server.tomcat.xxx
      server.tomcat.uri-encoding=UTF-8
      //其他的类似
      
    • 编写一个 WebServerFactoryCustomizer 嵌入式的servlet容器的定制器,ConfigurableServletWebServerFactory中有很多方法调用,来修改servlet容器的配置

          @Bean
          public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> customizationBean(){
              return new WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>() {
                  @Override
                  public void customize(ConfigurableServletWebServerFactory factory) {
                      factory.setPort(8083);
                  }
              };
          }
      
      

      注册三大组件

      1. 注册Servlet:ServletRegistrationBean

        //定制自己的servlet
        
        public class MyServlet extends HttpServlet {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                doPost(req, resp);
            }
        
            @Override
            protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                resp.getWriter().write("hello MyServlet");
            }
        }
        
      @Bean
      public ServletRegistrationBean MyServlet(){
          ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
          return registrationBean;
      }
      
    • 注册Filter

      //定制了Filter
      public class MyFilter implements Filter {
      
      @Bean
      public FilterRegistrationBean MyFilter(){
          FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
          filterRegistrationBean.setFilter(new MyFilter());
          filterRegistrationBean.setUrlPatterns(Arrays.asList("/myServlet","/hello"));
          return filterRegistrationBean;
      }
      
    • 注册Listener

      //定制监听器
      public class MyListener implements ServletContextListener {
      
      @Bean
      public ServletListenerRegistrationBean MyListener(){
          ServletListenerRegistrationBean<MyListener> myListenerServletRegistrationBean = new ServletListenerRegistrationBean<MyListener>(new MyListener());
          return  myListenerServletRegistrationBean;
      }
      

    Spring帮我们自动配置SpringMVC的时候,自动的注册SpringMVC的前端控制器;DidpatcherServlet

    @Bean(
        name = {"dispatcherServletRegistration"}
    )
    @ConditionalOnBean(
        value = {DispatcherServlet.class},
        name = {"dispatcherServlet"}
    )
    public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
        DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
        //        private String path = "/";
        //		   默认拦截所有路径,包含静态资源
        //可以通过server.servletPath来修改拦截请求
        registration.setName("dispatcherServlet");
        registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
        multipartConfig.ifAvailable(registration::setMultipartConfig);
        return registration;
    }
    

    SpringBoot切换其他的Servlet容器

    SpringBoot的ConfigurableServletWebServerFactory 有Jetty,Undertow,Tomcat三个实现类

    默认支持这三种容器:

    • Tomcat

    • Jetty(长连接)-- web聊天

    • Undertow(不支持JSP)

    移除tomcat后注入新的依赖切换

    嵌入式Servlet容器自动配置原理(2.3.0版本)

    EmbeddedWebServerFactoryCustomizerAutoConfiguration:嵌入式的servlet容器自动配置

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnWebApplication
    @EnableConfigurationProperties({ServerProperties.class})
    public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
    
    1. 该类中有对应支持的Servlet容器内部类

      • TomcatWebServerFactoryCustomizerConfiguration
      • JettyWebServerFactoryCustomizerConfiguration
      • UndertowWebServerFactoryCustomizerConfiguration
      • NettyWebServerFactoryCustomizerConfiguration
    2. 每个类都有@ConditionalOnClass注解,判断是否引入了对应的依赖,引入了对应依赖的即创建一个xxxWebServerFactoryCustomizer来构建Servlet容器工厂

      //以Tomcat为例
      @Configuration(
          proxyBeanMethods = false
      )
      @ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
      public static class TomcatWebServerFactoryCustomizerConfiguration {
          public TomcatWebServerFactoryCustomizerConfiguration() {
          }
      
          @Bean
          public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
              return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
          }
      }
      
    3. 在TomcatWebServerFactoryCustomizer中的customize里从容器中获取了对应的ConfigurableWebServerFactory工厂来构建容器配置,被SpringBoot使用,以下以Tomcat为例

      public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
          private final Environment environment;
          private final ServerProperties serverProperties;
      
          public void customize(ConfigurableTomcatWebServerFactory factory) {
      

      ConfigurableWebServerFactory 接口如下:

    1. 而SpringBoot已经通过ServletWebServerFactoryConfiguration 配置类放置了支持容器对应的ConfigurableWebServerFactory 实现类,如果用户配置了自己的servlet容器,SpringBoot则不放入默认的Servlet容器

      • ServletWebServerFactoryConfiguration 配置Servlet工厂,内部定义了EmbeddedTomcat,EmbeddedJetty,EmbeddedUndertow三个内部类,以下EmbeddedTomcat为例,@ConditionalOnMissingBean用来判断,如果用户没有定义自己的ServletWebServerFactory,则生效配置默认的容器TomcatServletWebServerFactory,被TomcatWebServerFactoryCustomizer调用后创建了一个Tomcat对象
      @Configuration(
          proxyBeanMethods = false
      )
      class ServletWebServerFactoryConfiguration {
          @Configuration(
              proxyBeanMethods = false
          )
          @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
          @ConditionalOnMissingBean(
              value = {ServletWebServerFactory.class},
              search = SearchStrategy.CURRENT
          )
          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
      public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
          //创建Tomcat对象
              Tomcat tomcat = new Tomcat();
          //配置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);
          //将配置好的Tomcat传入进去,返回一个TomcatWebServer,在TomcatWebServer对象的initialize()中有个关键的代码
          //this.tomcat.start();启动Tomcat
              return this.getTomcatWebServer(tomcat);
          }
      
      

    用户定义的配置修改是怎么生效的

    1. 配置文件的修改是直接修改ServletProperties的属性,对默认配置进行修改,使用的还是默认的容器

    2. 在EmbeddedWebServerFactoryCustomizerAutoConfiguration的@EnableConfigurationProperties注解里传入了一个@Import({EnableConfigurationPropertiesRegistrar.class}) 配置注册器:获取我们的配置来初始化

      class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
          EnableConfigurationPropertiesRegistrar() {
          }
      
          public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
              //在getTypes方法中获取了
              registerInfrastructureBeans(registry);
              ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
              //在该方法获取我们自定义的配置并初始.....(待研究)
              this.getTypes(metadata).forEach(beanRegistrar::register);
          }
          private Set<Class<?>> getTypes(AnnotationMetadata metadata) {
          return (Set)metadata.getAnnotations().stream(EnableConfigurationProperties.class).flatMap((annotation) -> {
              return Arrays.stream(annotation.getClassArray("value"));
          }).filter((type) -> {
              return Void.TYPE != type;
          }).collect(Collectors.toSet());
      }
      

    嵌入式Servlet容器启动原理

    1. SpringBoot应用启动运行run方法

    2. 先创建IOC容器对象,初始化容器,并创建容器中的每一个组件

      context = this.createApplicationContext();
      exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
      this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      this.refreshContext(context);
      
      //判断创建的是不是web应用及其类型,创建相应的对象容器
      protected ConfigurableApplicationContext createApplicationContext() {
          Class<?> contextClass = this.applicationContextClass;
          if (contextClass == null) {
              try {
                  switch(this.webApplicationType) {
                  case SERVLET:
                      contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                      break;
                  case REACTIVE:
                      contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                      break;
                  default:
                      contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                  }
              }
          }
      
    3. 调用refresh刷新创建好的容器,this.refresh((ApplicationContext)context);调用了父容器的刷新方法

      private void refreshContext(ConfigurableApplicationContext context) {
              this.refresh((ApplicationContext)context);
      
      try {
          this.postProcessBeanFactory(beanFactory);
          this.invokeBeanFactoryPostProcessors(beanFactory);
          this.registerBeanPostProcessors(beanFactory);
          this.initMessageSource();
          this.initApplicationEventMulticaster();
          this.onRefresh();
          this.registerListeners();
          this.finishBeanFactoryInitialization(beanFactory);
          this.finishRefresh();
      
    4. onRefresh方法:web的ioc容器重写了onRefresh方法,创建了webserver

       protected void onRefresh() {
              super.onRefresh();
      
              try {
                  this.createWebServer();
              } catch (Throwable var2) {
                  throw new ApplicationContextException("Unable to start web server", var2);
              }
          }
      
    5. webserver创建第一步获取了嵌入式servlet容器工厂ServletWebServerFactory,就是上述提到的调用嵌入式servlet自动配置来判断创建哪个serlvet容器工厂

      private void createWebServer() {
              WebServer webServer = this.webServer;
              ServletContext servletContext = this.getServletContext();
              if (webServer == null && servletContext == null) {
                  ServletWebServerFactory factory = this.getWebServerFactory();
      
    6. 触发上述的EnableConfigurationPropertiesRegistrar作用,配置Servlet容器

    7. 使用容器工厂获取嵌入式的servlet容器

      this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
      
    8. 嵌入式的servlet容器创建对象并启动Servlet容器,再将ioc容器中剩下没有创建出来的对象获取出来,最后才完成ioc容器的刷新

      Tomcat tomcat = new Tomcat();
      

    总结:在ioc容器启动创建嵌入式的serlver容器

  • 相关阅读:
    性能百万/s:腾讯轻量级全局流控方案详解
    Swagger2
    shiro 入门
    01、单例模式
    02、工厂方法
    04、模板模式
    13、Adapter 适配器
    14、迭代器
    Java 面向切面 AOP
    spring boot 中使用 Redis 与 Log
  • 原文地址:https://www.cnblogs.com/JIATCODE/p/13169999.html
Copyright © 2011-2022 走看看