1. 程式人生 > >SpringBoot中嵌入式Servlet容器啟動原理;

SpringBoot中嵌入式Servlet容器啟動原理;

什麼時候建立嵌入式的Servlet容器工廠?什麼時候獲取嵌入式的Servlet容器並啟動Tomcat;

獲取嵌入式的Servlet容器工廠:

1)、SpringBoot應用啟動執行run方法

2)、refreshContext(context);SpringBoot重新整理IOC容器【建立IOC容器物件,並初始化容器,建立容器中的每一個元件】;如果是web應用建立AnnotationConfigEmbeddedWebApplicationContext,否則:AnnotationConfigApplicationContext

3)、refresh(context);重新整理剛才建立好的ioc容器;

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

4)、 onRefresh(); web的ioc容器重寫了onRefresh方法

5)、webioc容器會建立嵌入式的Servlet容器;createEmbeddedServletContainer();

6)、獲取嵌入式的Servlet容器工廠:

EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();

從ioc容器中獲取EmbeddedServletContainerFactory 元件;TomcatEmbeddedServletContainerFactory建立物件,後置處理器一看是這個物件,就獲取所有的定製器來先定製Servlet容器的相關配置;

7)、使用容器工廠獲取嵌入式的Servlet容器:this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer());

8)、嵌入式的Servlet容器建立物件並啟動Servlet容器;

先啟動嵌入式的Servlet容器,再將ioc容器中剩下沒有創建出的物件獲取出來;

IOC容器啟動建立嵌入式的Servlet容器

9、使用外接的Servlet容器

嵌入式Servlet容器:應用打成可執行的jar

	優點:簡單、便攜;

	缺點:預設不支援JSP、優化定製比較複雜(使用定製器【ServerProperties、自定義EmbeddedServletContainerCustomizer】,自己編寫嵌入式Servlet容器的建立工廠【EmbeddedServletContainerFactory】);

外接的Servlet容器:外面安裝Tomcat—應用war包的方式打包;

步驟

1)、必須建立一個war專案;(利用idea建立好目錄結構)

2)、將嵌入式的Tomcat指定為provided;

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-tomcat</artifactId>
   <scope>provided</scope>
</dependency>

3)、必須編寫一個SpringBootServletInitializer的子類,並呼叫configure方法

public class ServletInitializer extends SpringBootServletInitializer {

   @Override
   protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
       //傳入SpringBoot應用的主程式
      return application.sources(SpringBoot04WebJspApplication.class);
   }

}

4)、啟動伺服器就可以使用;

原理

jar包:執行SpringBoot主類的main方法,啟動ioc容器,建立嵌入式的Servlet容器;

war包:啟動伺服器,伺服器啟動SpringBoot應用【SpringBootServletInitializer】,啟動ioc容器;

servlet3.0(Spring註解版):

8.2.4 Shared libraries / runtimes pluggability:

規則:

1)、伺服器啟動(web應用啟動)會建立當前web應用裡面每一個jar包裡面ServletContainerInitializer例項:

2)、ServletContainerInitializer的實現放在jar包的META-INF/services資料夾下,有一個名為javax.servlet.ServletContainerInitializer的檔案,內容就是ServletContainerInitializer的實現類的全類名

3)、還可以使用@HandlesTypes,在應用啟動的時候載入我們感興趣的類;

流程:

1)、啟動Tomcat

2)、org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer:

Spring的web模組裡面有這個檔案:org.springframework.web.SpringServletContainerInitializer

3)、SpringServletContainerInitializer將@HandlesTypes(WebApplicationInitializer.class)標註的所有這個型別的類都傳入到onStartup方法的Set<Class<?>>;為這些WebApplicationInitializer型別的類建立例項;

4)、每一個WebApplicationInitializer都呼叫自己的onStartup;

5)、相當於我們的SpringBootServletInitializer的類會被建立物件,並執行onStartup方法

6)、SpringBootServletInitializer例項執行onStartup的時候會createRootApplicationContext;建立容器

protected WebApplicationContext createRootApplicationContext(
      ServletContext servletContext) {
    //1、建立SpringApplicationBuilder
   SpringApplicationBuilder builder = createSpringApplicationBuilder();
   StandardServletEnvironment environment = new StandardServletEnvironment();
   environment.initPropertySources(servletContext, null);
   builder.environment(environment);
   builder.main(getClass());
   ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
   if (parent != null) {
      this.logger.info("Root context already created (using as parent).");
      servletContext.setAttribute(
            WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
      builder.initializers(new ParentContextApplicationContextInitializer(parent));
   }
   builder.initializers(
         new ServletContextApplicationContextInitializer(servletContext));
   builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
    
    //呼叫configure方法,子類重寫了這個方法,將SpringBoot的主程式類傳入了進來
   builder = configure(builder);
    
    //使用builder建立一個Spring應用
   SpringApplication application = builder.build();
   if (application.getSources().isEmpty() && AnnotationUtils
         .findAnnotation(getClass(), Configuration.class) != null) {
      application.getSources().add(getClass());
   }
   Assert.state(!application.getSources().isEmpty(),
         "No SpringApplication sources have been defined. Either override the "
               + "configure method or add an @Configuration annotation");
   // Ensure error pages are registered
   if (this.registerErrorPageFilter) {
      application.getSources().add(ErrorPageFilterConfiguration.class);
   }
    //啟動Spring應用
   return run(application);
}

7)、Spring的應用就啟動並且建立IOC容器

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      analyzers = new FailureAnalyzers(context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
       
       //重新整理IOC容器
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
   }
}

啟動Servlet容器,再啟動SpringBoot應用