SpringBoot2--外部Servlet容器

前言

嵌入式Servlet容器的优点是简单、便携,缺点是默认不支持JSP、优化定制比较复杂(自定义定制器或者工厂),所以有时需要使用外部Servlet容器,应用war包的形式打包

一、外部Servlet容器工作原理

jar包:执行SpringBoot主类的main方法,启动ioc容器,创建嵌入式的Servlet容器;
war包:启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,启动ioc容器;

1.servlet3.0规则

1)、服务器启动(web应用启动)会在当前web应用里面的每一个jar包里面创建一个ServletContainerInitializer实例:
2)、每个jar包的META-INF/services文件夹下,有一个名为 javax.servlet.ServletContainerInitializer的文件,存放着ServletContainerInitializer的实现类的全类名
3)、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;

2.启动流程

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
在这里插入图片描述
org.springframework.web.SpringServletContainerInitializer

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {

    public void onStartup(@Nullable 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 {
                        initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

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

4)、每一个WebApplicationInitializer都调用自己的onStartup;
SpringBootServletInitializer就是一个WebApplicationInitializer,所以它会调用自己的onStartup

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                initializer.onStartup(servletContext);
            }

5)、SpringBootServletInitializer的onStartup在执行过程中,会调用builder = this.configure(builder);传入一个主程序类,用来创建和启动Spring应用

public abstract class SpringBootServletInitializer implements WebApplicationInitializer {

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

    }


    protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
        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);
        builder = this.configure(builder);
        builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
        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));
        }

        application.setRegisterShutdownHook(false);
        return this.run(application);
    }

6)、configure这个方法在ServletInitializer中重写了,ServletInitializer把Application传入configure,创建这个Spring的应用并且启动,然后创建ioc容器

public class ServletInitializer extends SpringBootServletInitializer {

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

}

二、创建外部Servlet容器项目

1.首先新建一个springboot项目,打成war包,将嵌入式的Tomcat指定为provided;
它会有一个ServletInitializer类

public class ServletInitializer extends SpringBootServletInitializer {

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

}

2.生成目录结构,点击project Structure按钮,选中Moduels 点击Web,然后生成web resorure Directory Path
在这里插入图片描述
3.生成web.xml文件,这个目录\src\main\webapp\WEB-INF\web.xml
在这里插入图片描述
4.点击edit configurationgs ,点+号,添加一个Tomcat Server并且命名,然后点Configure选中一个Tomcat,设置访问端口
在这里插入图片描述
5.点击Deployment,选+号,选中一个项目,点击Ok
在这里插入图片描述
6.选择刚刚设置的Tomcat ,然后运行
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值