Spring Boot 使用外置的 Servlet 容器&启动原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Java_Glory/article/details/90035520

嵌入式 Servlet 容器:应用打成可执行的jar包

     优点:简单,便携

     缺点:默认不支持JSP,优化定制比较复杂(使用定制器【ServerProperties、自定义EmbeddedServletContainerCustomizer】,自己编写嵌入式Servlet容器的创建工厂【EmbeddedServletContainerFactory】 );

外置的Servlet容器:外面安装Tomcat环境---应用以war包的方式打包;

步骤:

     1,必须创建一个war项目;(利用idea创建好目录结构)

          详情参见:Spring Boot 创建Packaging为War包的应用并启动

     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容器;

规则:

     1,服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例:

     2,ServletContainerInitializer 的实现放在jar包的 META-INF/services 文件夹下,有一个名为javax.servlet.ServletContainerInitializer 的文件,内容就是 ServletContainerInitializer 的实现类的全类名

     3,还可以使用 @HandlesTypes,在应用启动的时候加载感兴趣的类;

流程:

     1,启动Tomcat

     2,maven本地仓库下\org\springframework\spring-web\4.3.23.RELEASE\spring-web-4.3.23.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应用

猜你喜欢

转载自blog.csdn.net/Java_Glory/article/details/90035520