SpringBoot使用外部Servelet容器及外部servlet容器启动SpringBoot应用原理

前言

嵌入式Servlet容器:应用打成可执行的jar(内置Servlet打成JAR包,而外置打成WAR包)

  优点:简单、便携;

  缺点:默认不支持JSP、优化定制比较复杂

  (2.x之前的使用定制器【ServerProperties、自定义EmbeddedServletContainerCustomizer】,自己编写嵌入式Servlet容器的创建工厂        【EmbeddedServletContainerFactory】);

配置外部servlet容器的方式

1、新建项目时,选择WAR包的打包方式;
2、建立WEB应用目录结构,如下图,可手工建,也可以通过红框中的工具

SpringBoot使用外部Servelet容器及外部servlet容器启动SpringBoot应用原理_第1张图片

 注意 Application Context 的配置

SpringBoot使用外部Servelet容器及外部servlet容器启动SpringBoot应用原理_第2张图片

 

 

 之前版本如下

SpringBoot使用外部Servelet容器及外部servlet容器启动SpringBoot应用原理_第3张图片

 

 

 注意以下模块的变化

①maven中scope的范围为provided


   org.springframework.boot
   spring-boot-starter-tomcat
   provided

 ②新增一个springboot的初始化器

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        //传入SpringBoot应用的主程序
        return application.sources(Demo2Application.class);
    }

}    

 外部servlet容器启动SpringBoot应用原理

外部servlet如何启动springboot容器,要从servlet3.0之后的启动规则说起

servlet3.0规则:

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

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

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

  原文如下:

SpringBoot使用外部Servelet容器及外部servlet容器启动SpringBoot应用原理_第4张图片

 

 

 javax.servlet.ServletContainerInitializer SpringBoot使用外部Servelet容器及外部servlet容器启动SpringBoot应用原理_第5张图片

 

 

 启动流程

①启动外部的Tomcat

②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

③SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set>;为这些WebApplicationInitializer类型的类创建实例;

④每一个WebApplicationInitializer都调用自己的onStartup;

 SpringBoot使用外部Servelet容器及外部servlet容器启动SpringBoot应用原理_第6张图片

 

 

 

⑤相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法

⑥SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建容器

public void onStartup(@Nullable Set> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List 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 {
                        initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).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();
                initializer.onStartup(servletContext);
            }

        }
    }

 

 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(); 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(AnnotationConfigServletWebServerApplicationContext.class);
    //调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来 builder = this.configure(builder); builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)}); //使用builder创建一个Spring应用
     SpringApplication application = builder.build(); if (application.getAllSources().isEmpty() && MergedAnnotations.from(this.getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) { application.addPrimarySources(Collections.singleton(this.getClass())); } Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation"); if (this.registerErrorPageFilter) { application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class)); }     //启动Spring应用 return this.run(application); }

⑦Spring的应用就启动并且创建IOC容器

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

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
       //刷新IOC容器 this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }

 

原理

jar包:执行SpringBoot主类的main方法,启动ioc容器,创建嵌入式的Servlet容器;

war包:启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,启动ioc容器;

==启动Servlet容器,再启动SpringBoot应用==

你可能感兴趣的:(SpringBoot使用外部Servelet容器及外部servlet容器启动SpringBoot应用原理)