SpringBoot之使用外置Servlet容器

一、SpringBoot嵌入式Servlet容器与外置Servlet容器的比较

  • 嵌入式Servlet容器应用将程序打成Jar包,外置Servlet容器应用将程序打成War包。
  • 嵌入式Servlet容器的优点:简单、便携。缺点:默认不支持JSP,优化定制较复杂。

 嵌入式Servlet容器优化定制的方法:

   ① 使用定制器:ServerProperties、自定义 EmbeddedServletContainerCustomizer
  ② 自己编写嵌入式Servlet容器的创建工厂 【EmbeddedServletContainerFactory】

二、如何使用外置Servlet容器的步骤

1、创建一个War包项目

;File——》New——Project,选中Spring Initializr,然后单击Next,进入到如下页面,将默认的Jar包改成War,然后Next,直到Finish。
在这里插入图片描述
创建出来的目录结构:
在这里插入图片描述
  由于创建的是War包项目,所以以上的目录结构还不完整,我们需要手动创建web.xml和webapp。有两种创建方式:

① 手动在src/main下创建webapp目录及在/src/main/webapp/WEB-INF下创建web.xml文件。

② 在IDEA的Project Structure中完善目录结构。
在这里插入图片描述

点击Module,选中web,来到如下页面:
在这里插入图片描述
双击上图圈出来的url处,弹出以下对话框:
在这里插入图片描述
单击OK,选择Yes。
在这里插入图片描述
还是在Project Structure页面,点击如下图所示的“+”,弹出如下对话框,修改生成web.xml文件的目录/src/main/webapp,然后点击OK。
在这里插入图片描述
在上一步中点击OK后,先点击Apply,然后选择OK,即可创建出webapp目录和web.xml文件。
在这里插入图片描述
完善后的目录结构如下:
在这里插入图片描述

2、将嵌入式的tomcat指定为provided

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

3、必须写一个SpringBootServletInitializer 的实现子类。

  目的是调用configure方法,传入springboot应用的主程序。

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SpringBootWebJspApplication.class);
    }
}

4、启动服务器

三、外部Servlet启动SpringBoot应用原理(1.5.x)

  • 将项目打成 jar包启动方式:执行SpringBoot主类的main方法,启动IOC容器,创建嵌入式的Servlet容器。
  • 将项目打成 war包启动方式:启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,然后启动IOC容器。

  在Spring3.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的文件,文件内容是:

org.springframework.web.SpringServletContainerInitializer

相当于在web应用启动的时候启动SpringServletContainerInitializer
【3】SpringServletContainerInitializer@HandlesTypes(WebApplicationInitializer.class)标注的所有类型的类(感兴趣的类)都传入到onStartup方法的Set<Class<?>> webAppInitializerClasses,为这些WebApplicationInitializer类型的类(不是接口、不是抽象类)创建实例。
【4】每一个WebApplicationInitializer都调用自己的onStartup,相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法。WebApplicationInitializer是一个接口,它的实现类如下:
在这里插入图片描述


//Set<Class<?>> webAppInitializerClasses表示感兴趣的类
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
    List<WebApplicationInitializer> initializers = new LinkedList();
    Iterator var4;
    if (webAppInitializerClasses != null) {
       var4 = webAppInitializerClasses.iterator();

    while(var4.hasNext()) {
       Class<?> waiClass = (Class)var4.next();
       //如果不死接口或者抽象类
       if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
          try {
              //加入到WebApplicationInitializer对象的集合中
                initializers.add((WebApplicationInitializer)waiClass.newInstance());
              } catch (Throwable var7) {
                  throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
              }
            }
          }
       }
       
     if (initializers.isEmpty()) {
        servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
     } else {
          servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
          AnnotationAwareOrderComparator.sort(initializers);
          var4 = initializers.iterator();

          while(var4.hasNext()) {
              WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
              //每一个WebApplicationInitializer对象调用自己的onStartup方法创建SpringBootServletInitializer对象
              initializer.onStartup(servletContext);
          }
      }
}
public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SpringBootWebJspApplication.class);
    }

}

【5】SpringBootServletInitializer实例执行onStartup方法的时候会调用createRootApplicationContext方法创建容器

public void onStartup(ServletContext servletContext) throws ServletException {
        this.logger = LogFactory.getLog(this.getClass());
        WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
        if (rootAppContext != null) {
            servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                public void contextInitialized(ServletContextEvent event) {
                }
            });
        } else {
            this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
        }

    }

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
//1、创建SpringApplicationBuilder对象
    SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
    StandardServletEnvironment environment = new StandardServletEnvironment();
    environment.initPropertySources(servletContext, (ServletConfig)null);
    builder.environment(environment);
    builder.main(this.getClass());
    ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
    if (parent != null) {
      this.logger.info("Root context already created (using as parent).");
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
      builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
     }

      builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
      builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
      //调用configure方法,在子类重写了这个方法,传入SpringBoot的主程序类
      builder = this.configure(builder);
      //使用builder创建spring应用
      SpringApplication application = builder.build();
      if (application.getSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
          application.getSources().add(this.getClass());
      }

      Assert.state(!application.getSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
      if (this.registerErrorPageFilter) {
         application.getSources().add(ErrorPageFilterConfiguration.class);
      }
      //启动spring应用
     return this.run(application);
}

protected SpringApplicationBuilder createSpringApplicationBuilder() {
    return new SpringApplicationBuilder(new Object[0]);
}

【6】启动spring应用,并创建IOC容器

 public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            new FailureAnalyzers(context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //刷新IOC容器
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            listeners.finished(context, (Throwable)null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
            throw new IllegalStateException(var9);
        }
    }

一句话总结:先启动Servlet容器,启动spring应用,再创建IOC容器。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值