【SpringBoot】Servlet容器


title: 【SpringBoot】Servlet容器
date: 2017-08-31 21:25:12
tags:

  • Java
  • Spring
    categories: Spring

記得自己看 Spring Boot 源碼的初衷是對(duì)部署時(shí)不需要額外的 Servlet 容器的好奇,好像看著看著關(guān)注到了其他細(xì)節(jié)。挖了幾個(gè)坑就跳過(guò)了,今天把之前關(guān)于 Spring Boot 與 Servlet 容器的坑填一下。

先回(rang)顧(wo)一(xiang)下(xiang)之前與 Web 環(huán)境相關(guān)的內(nèi)容:

Servlet 容器啟動(dòng)

【SpringBoot】容器啟動(dòng) 中提到的創(chuàng)建處理 web 環(huán)境對(duì)應(yīng)的 Spring 容器實(shí)際創(chuàng)建的就是 AnnotationConfigEmbeddedWebApplicationContext,這個(gè)類是 ApplicationContext 的子類。其分別在 onRefresh 和 finishRefresh 方法中創(chuàng)建和啟動(dòng) Servlet 容器:

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
      createEmbeddedServletContainer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start embedded container", ex);
   }
}

@Override
protected void finishRefresh() {
  super.finishRefresh();
  EmbeddedServletContainer localContainer = startEmbeddedServletContainer();
  if (localContainer != null) {
    publishEvent(
    new EmbeddedServletContainerInitializedEvent(this, localContainer));
  }
}

在 Spring 容器生命周期里與 Servlet 容器相關(guān)的邏輯封裝在以上兩個(gè)方法,分別用以創(chuàng)建和啟動(dòng) Servlet 容器。

Servlet 容器創(chuàng)建細(xì)節(jié)

上面說(shuō)到 AnnotationConfigEmbeddedWebApplicationContext 中創(chuàng)建 Servlet 容器,具體的細(xì)節(jié)如下:

// AnnotationConfigEmbeddedWebApplicationContext
private void createEmbeddedServletContainer() {
   EmbeddedServletContainer localContainer = this.embeddedServletContainer;
   ServletContext localServletContext = getServletContext();
   if (localContainer == null && localServletContext == null) {
      // 獲取 Servlet 容器工廠
      EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
      // 獲取 Servlet 容器初始化器
      // 使用 Servlet 容器初始化器創(chuàng)建 Servlet 容器
      this.embeddedServletContainer = 
        containerFactory.getEmbeddedServletContainer(getSelfInitializer());
   }
   else if (localServletContext != null) {
      try {
         getSelfInitializer().onStartup(localServletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context",
               ex);
      }
   }
   initPropertySources();
}

細(xì)節(jié)可以分為:

  1. 獲取 Servlet 容器工廠
  2. 獲取 Servlet 容器初始化器
  3. 創(chuàng)建 Servlet 容器

獲取 Servlet 容器工廠

// AnnotationConfigEmbeddedWebApplicationContext

protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
   // Use bean names so that we don't consider the hierarchy
   String[] beanNames = getBeanFactory()
         .getBeanNamesForType(EmbeddedServletContainerFactory.class);
   // 省略異常
   return getBeanFactory().getBean(beanNames[0],
         EmbeddedServletContainerFactory.class);
}

直接從 Spring 容器里獲取 Servlet 容器工廠。一時(shí)之間卡住了不知道 EmbeddedServletContainerFactory 這個(gè)類時(shí)怎么注冊(cè)到 Spring 容器中的。在網(wǎng)上查了一下發(fā)現(xiàn)是 META-INF/spring.factories 里指定了 EmbeddedServletContainerAutoConfiguration 這個(gè)配置類。而 EmbeddedServletContainerFactory 是 EmbeddedServletContainerAutoConfiguration 這個(gè)自動(dòng)化配置類中被注冊(cè)到 Spring 容器中的,關(guān)于配置類的注冊(cè)我又要挖坑了。EmbeddedServletContainerAutoConfiguration:

@AutoConfigureOrder(-2147483648)
@Configuration
@ConditionalOnWebApplication  // 在Web環(huán)境下起作用
@Import({EmbeddedServletContainerAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class EmbeddedServletContainerAutoConfiguration {
    public EmbeddedServletContainerAutoConfiguration() {
    }
    
    // 在 import 中導(dǎo)入該類
    // 主要作用是注冊(cè) EmbeddedServletContainerCustomizerBeanPostProcessor,
    // ErrorPageRegistrarBeanPostProcessor
    public static class BeanPostProcessorsRegistrar 
      implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
        // 省略
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                            BeanDefinitionRegistry registry) {
            if(this.beanFactory != null) {
                // 注冊(cè) EmbeddedServletContainerCustomizerBeanPostProcessor
                this.registerSyntheticBeanIfMissing(
                  registry, "embeddedServletContainerCustomizerBeanPostProcessor", 
                  EmbeddedServletContainerCustomizerBeanPostProcessor.class);
                // 注冊(cè) ErrorPageRegistrarBeanPostProcessor
                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);
            }

        }
    }

    // 省略 Jetty,Undertow 配置

    @Configuration
    @ConditionalOnClass({Servlet.class, Tomcat.class})
    @ConditionalOnMissingBean(
        value = {EmbeddedServletContainerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedTomcat {
        public EmbeddedTomcat() {
        }

        @Bean
        public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
            // 注冊(cè) EmbeddedServletContainerFactory 的實(shí)現(xiàn)類
            return new TomcatEmbeddedServletContainerFactory();
        }
    }
}

當(dāng)滿足特定條件時(shí)會(huì)注冊(cè)具體的 EmbeddedServletContainerFactory 實(shí)現(xiàn)類,例如 TomcatEmbeddedServletContainerFactory。

這里還看到 注冊(cè)了 EmbeddedServletContainerCustomizerBeanPostProcessor 和 ErrorPageRegistrarBeanPostProcessor 。簡(jiǎn)單看了一下 EmbeddedServletContainerCustomizerBeanPostProcessor,這是一個(gè)基本的 BeanPostProcessor,具體作用是對(duì) EmbeddedServletContainerCustomizer 的實(shí)例進(jìn)行定制,具體的實(shí)現(xiàn)包括:ErrorPageCustomizer,TomcatWebSocketContainerCustomizer,ServerProperties 等。

// EmbeddedServletContainerCustomizerBeanPostProcessor 
public class EmbeddedServletContainerCustomizerBeanPostProcessor
      implements BeanPostProcessor, BeanFactoryAware {

   private ListableBeanFactory beanFactory;

   private List<EmbeddedServletContainerCustomizer> customizers;

   @Override
   public Object postProcessBeforeInitialization(Object bean, String beanName)
         throws BeansException {
      if (bean instanceof ConfigurableEmbeddedServletContainer) {
         // 處理ConfigurableEmbeddedServletContainer類型的bean
         postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
      }
      return bean;
   }

   private void postProcessBeforeInitialization(
         ConfigurableEmbeddedServletContainer bean) {
      // 對(duì)ConfigurableEmbeddedServletContainer類型的bean定制化處理
      for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
         customizer.customize(bean);
      }
   }

   private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
      if (this.customizers == null) {
         // 從容器中找出所有EmbeddedServletContainerCustomizer類型的bean
         this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
               this.beanFactory.getBeansOfType(EmbeddedServletContainerCustomizer.class,
                                               false, false).values());
         Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
         this.customizers = Collections.unmodifiableList(this.customizers);
      }
      return this.customizers;
   }

}

Servlet 容器初始化器

再回顧一下 Servlet 容器的創(chuàng)建核心邏輯:

containerFactory.getEmbeddedServletContainer(getSelfInitializer());

先獲取 Servlet 初始化器,然后創(chuàng)建 Servlet 容器

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
   return new ServletContextInitializer() {
      @Override
      public void onStartup(ServletContext servletContext) throws ServletException {
         selfInitialize(servletContext);
      }
   };
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
   // Servlet 容器準(zhǔn)備
   prepareEmbeddedWebApplicationContext(servletContext);
   // 初始 scopes
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
         beanFactory);
   WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
         getServletContext());
   existingScopes.restore();
   // 注冊(cè) web 相關(guān) bean
   WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
         getServletContext());
   // 初始化 servlet, filter, Listener 并注冊(cè)
   for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
      beans.onStartup(servletContext);
   }
}

// 獲取 ServletContext 初始化 bean
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
    new ServletContextInitializerBeans(getBeanFactory());
}
public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
   this.initializers = new LinkedMultiValueMap<Class<?>, ServletContextInitializer>();
   // 添加 ServletContextInitializer 類型的 bean
   addServletContextInitializerBeans(beanFactory);
   addAdaptableBeans(beanFactory);
   List<ServletContextInitializer> sortedInitializers = 
     new ArrayList<ServletContextInitializer>();
   for (Map.Entry<?, List<ServletContextInitializer>> entry : this.initializers.entrySet()) {
      AnnotationAwareOrderComparator.sort(entry.getValue());
      sortedInitializers.addAll(entry.getValue());
   }
   this.sortedList = Collections.unmodifiableList(sortedInitializers);
}

private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
   for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(
         beanFactory, ServletContextInitializer.class)) {
      addServletContextInitializerBean(initializerBean.getKey(),
            initializerBean.getValue(), beanFactory);
   }
}

private void addServletContextInitializerBean(String beanName,
      ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
   if (initializer instanceof ServletRegistrationBean) {
      // servlet
      Servlet source = ((ServletRegistrationBean) initializer).getServlet();
      addServletContextInitializerBean(Servlet.class, beanName, initializer,
            beanFactory, source);
   }
   else if (initializer instanceof FilterRegistrationBean) {
      // filter
      Filter source = ((FilterRegistrationBean) initializer).getFilter();
      addServletContextInitializerBean(Filter.class, beanName, initializer,
            beanFactory, source);
   }
   else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
      // filter
      String source = ((DelegatingFilterProxyRegistrationBean) initializer)
            .getTargetBeanName();
      addServletContextInitializerBean(Filter.class, beanName, initializer,
            beanFactory, source);
   }
   else if (initializer instanceof ServletListenerRegistrationBean) {
      // listener
      EventListener source = ((ServletListenerRegistrationBean<?>) initializer)
            .getListener();
      addServletContextInitializerBean(EventListener.class, beanName, initializer,
            beanFactory, source);
   }
   else {
      addServletContextInitializerBean(ServletContextInitializer.class, beanName,
            initializer, beanFactory, initializer);
   }
}

創(chuàng)建 Servlet 容器

以 TomcatEmbeddedServletContainerFactory 的創(chuàng)建容器方法為例:

@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
      ServletContextInitializer... initializers) {
   Tomcat tomcat = new Tomcat();
   File baseDir = (this.baseDirectory != null ? this.baseDirectory
         : createTempDir("tomcat"));
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   Connector connector = new Connector(this.protocol);
   tomcat.getService().addConnector(connector);
   customizeConnector(connector);
   tomcat.setConnector(connector);
   tomcat.getHost().setAutoDeploy(false);
   configureEngine(tomcat.getEngine());
   for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
   }
   prepareContext(tomcat.getHost(), initializers);
   return getTomcatEmbeddedServletContainer(tomcat);
}

上面的代碼完成了 Servlet 容器啟動(dòng)前所有的創(chuàng)建,配置動(dòng)作。

Servlet 容器啟動(dòng)

private EmbeddedServletContainer startEmbeddedServletContainer() {
   EmbeddedServletContainer localContainer = this.embeddedServletContainer;
   if (localContainer != null) {
      localContainer.start();
   }
   return localContainer;
}

啟動(dòng)的邏輯并不復(fù)雜,直接調(diào)用 Servlet 容器的 start 方法。

總結(jié)

Spring Boot 使用的內(nèi)嵌 Servlet 容器啟動(dòng)過(guò)程:

  1. 從 spring.factories 中讀取并注冊(cè)了 EmbeddedServletContainerAutoConfiguration 類
  2. EmbeddedServletContainerAutoConfiguration 注冊(cè)了 Servlet 容器工廠類
  3. 在 Spring 容器生命周期的 onRefresh 方法中開(kāi)始創(chuàng)建 Servlet 容器
    1. 獲取 Servlet 容器工廠
    2. 獲取 Servlet 相關(guān)初始化 bean
    3. 配置并創(chuàng)建 Servlet 容器
  4. 在 Spring 容器生命周期的 finishRefresh 方法中調(diào)用 Servlet 容器的 start 方法啟動(dòng)容器
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,273評(píng)論 6 342
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong閱讀 22,942評(píng)論 1 92
  • 1.1 spring IoC容器和beans的簡(jiǎn)介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器,...
    simoscode閱讀 6,851評(píng)論 2 22
  • 1.引言 最近在學(xué)習(xí)Spring、SpringMVC、Maven,想在Eclipse下構(gòu)建maven項(xiàng)目,不過(guò)由于...
    locoder閱讀 4,620評(píng)論 1 3

友情鏈接更多精彩內(nèi)容