使用外置Tomcat部署SpringBoot项目原理

/**
 * 原理:
 * <pre>
 *     javax.servlet规范提供的机制ServletContainerInitializers(SCIs)
 *     在META-INF/services/javax.servlet.ServletContainerInitializer文件中
 *     将ServletContainerInitializer实现类写在该文件中,Web容器会自动将这些实现类加载,并调用这些类的onStartup
 *     同时,还可以实现类中使用@HandlesTypes(Xxx.class)注解,将Xxx.class的所有子类(接口,实现类,抽象类)通过onStartup传递
 *     并且还会将ServletContext上下文传递过来
 * </pre>
 * <pre>
 *     其中,SpringServletContainerInitializer实现了ServletContainerInitializer接口
 *     并在spring-web\5.2.2.RELEASE\spring-web-5.2.2.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer注册了该实现类
 *     而SpringServletContainerInitializer又使用了HandlesTypes直接,此时,就会将Spring提供的WebApplicationInitializer接口的所有实现类传递,这就与Spring建立了连接
 *     @HandlesTypes(WebApplicationInitializer.class)
 *     public class SpringServletContainerInitializer implements ServletContainerInitializer {
 *
 *     }
 * </pre>
 * 使用步骤:
 * <pre>
 *  1. 将springboot项目打包成war包
 *          <packaging>war</packaging>
 *  2. 创建类继承SpringBootServletInitializer,可以重写configure方法设置自定义配置
 *  3. 将war部署到Tomcat中,和单独的springmvc项目一样
 * </pre>
 */

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

    @Override
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) {

        List<WebApplicationInitializer> initializers = new LinkedList<>();
        // 所有WebApplicationInitializer的实现类
        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    // 创建这些类的实例对象
                    initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                }
            }
        }
        // 如果不存在WebApplicationInitializer的实现类,就不需要处理
        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }
        // 如果存在WebApplicationInitializer的实现类
        // 排序
        AnnotationAwareOrderComparator.sort(initializers);
        // 回调每一个WebApplicationInitializer的onStartup方法
        for (WebApplicationInitializer initializer : initializers) {
            // SpringBootServletInitializer initializer;
            // 这里就包含了SpringBootServletInitializer,SpringBoot的初始化器
            initializer.onStartup(servletContext);
        }
    }

}

class SpringBootServletInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) {
        WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
        if (rootAppContext != null) {
            servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                @Override
                public void contextInitialized(ServletContextEvent event) {
                    // 无操作,因为应用程序上下文已经初始化
                }
            });
        }
    }

    // 创建Spring容器
    protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
        // 创建构建者模式创建SpringApplication
        SpringApplicationBuilder builder = this.createSpringApplicationBuilder() {
            return new SpringApplicationBuilder();
            {
                // 直接创建SpringApplication对象
                this.application = new SpringApplication(sources);

            }
        }
        // 设置main运行的类
        builder.main(getClass()) {
            this.application.setMainApplicationClass(mainApplicationClass);
        }
        // 查看是否创建过了容器,创建了则不再创建
        //  WebApplicationContext.class.getName() + ".ROOT";
        ApplicationContext parent = getExistingRootWebApplicationContext(servletContext) {
            Object context = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
            if (context instanceof ApplicationContext) {
                return (ApplicationContext) context;
            }
            return null;
        }
        if (parent != null) {
            // 重置Context对象
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
            // 添加一个初始化器,该类就是为创建的Context设置父容器
            builder.initializers(new ParentContextApplicationContextInitializer(parent));
        }
        // 添加一个初始化器,就是将创建的上下文重新设置到servletContext
        // 上面几行代码就是在判断之前是否存在Context对象,如果存在,将之前的Context对象作为即将创建Context的父容器
        builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
        // 设置要创建的上下文对象类型
        builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
        // 空实现,对builder进行自定义配置
        builder = this.configure(builder);
        // 添加ApplicationListener监听器,该类就是将ServletContext的相关属性设置到环境对象中
        builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
        // 创建SpringApplication对象
        SpringApplication application = builder.build() {
            // 将当前设置的配置类设置为主要的配置类
            this.application.addPrimarySources(this.sources);
            return this.application;
        }
        // 如果没有设置解析的配置类,但是当前类中存在@Configuration注解
        // 将当前类设置为需要解析的配置类
        if (application.getAllSources().isEmpty() && MergedAnnotations.from(getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
            application.addPrimarySources(Collections.singleton(getClass()));
        }
        // 如果需要注册错误页面的过滤器
        if (this.registerErrorPageFilter) {
            // 添加一个配置类,该类就是注册org.springframework.boot.web.servlet.support.ErrorPageFilter
            application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
        }
        // 调用SpringBootApplication.run方法,详细的请看文章SpringBoot运行原理
        return application.run();
    }
}

// 这是自己写的类,继承了SpringBootServletInitializer,因此也会执行
@SpringBootApplication
public class App extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        builder.sources(App.class);
        return super.configure(builder);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值