欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Springboot 2使用外部Tomcat源码分析

程序员文章站 2024-01-26 20:58:52
Springboot 使用外部 Tomcat 1.修改 pom.xml,改为打 war 包 2.将 Springboot 内置 tomcat 作用域改为 3.重写 SpringBootServletInitializer 4.maven 打包出 war 包后,放到 tomcat 的 webapps ......

springboot 使用外部 tomcat

1.修改 pom.xml,改为打 war 包
<packaging>war</packaging>
2.将 springboot 内置 tomcat 作用域改为provided

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-tomcat</artifactid>
    <scope>provided</scope>
</dependency>

3.重写 springbootservletinitializer

@springbootapplication
public class bootstrap extends springbootservletinitializer {
  public static void main(string[] args) {
    springapplication.run(bootstrap.class, args);
  }

  @override
  protected springapplicationbuilder configure(springapplicationbuilder builder) {
    return builder.sources(bootstrap.class);
  }
}

4.maven 打包出 war 包后,放到 tomcat 的 webapps 目录下即可。

如果要访问该 war 包的接口,默认需要在 url 加项目名作为前缀,例如:http://localhost:8080/{项目名}/users/123456

原理分析

servletcontainerinitializer

servlet 容器启动时,会扫描当前应用每个 jar 包路径meta-inf\services下的文件javax.servlet.servletcontainerinitializer,其文件内容就是 servletcontainerinitializer 的实现类全类名,并调用其 onstartup() 方法。比如,在 spring-web 包下,该文件内容就是org.springframework.web.springservletcontainerinitializer,其源码如下:

// 容器启动时,将 webapplicationinitializer 的所有子类传递至 webappinitializerclasses
@handlestypes(webapplicationinitializer.class)
public class springservletcontainerinitializer implements servletcontainerinitializer {
    /**
     * @param webappinitializerclasses @handlestypes 导入的类
     * @param servletcontext 当前 web 应用 servlet 上下文
     */
    @override
    public void onstartup(@nullable set<class<?>> webappinitializerclasses, servletcontext servletcontext) throws servletexception {
        list<webapplicationinitializer> initializers = new linkedlist<>();
        for (class<?> waiclass : webappinitializerclasses) {
            // 过滤出可用的 webapplicationinitializer
            if (!waiclass.isinterface() && !modifier.isabstract(waiclass.getmodifiers()) && webapplicationinitializer.class.isassignablefrom(waiclass)) {
                initializers.add((webapplicationinitializer)reflectionutils.accessibleconstructor(waiclass).newinstance());
            }
        }
        for (webapplicationinitializer initializer : initializers) {
            initializer.onstartup(servletcontext);
        }
    }
}

容器启动时,执行 springservletcontainerinitializer.onstartup() 方法,@handlestypes 注解声明了 webapplicationinitializer 的所有子类(在前面的示例中,启动类 bootstrap 实现的 springbootservletinitializer 就是它的一个实现)会被传递给方法的参数 webappinitializerclasses。

onstartup() 方法会过滤出 webappinitializerclasses 中可用的 webapplicationinitializer 子类 bootstrap,然后回调 springbootservletinitializer 的 onstartup() 方法,其源码如下:

public void onstartup(servletcontext servletcontext) throws servletexception {
    webapplicationcontext rootappcontext = createrootapplicationcontext(servletcontext);
}
protected webapplicationcontext createrootapplicationcontext(servletcontext servletcontext) {
    springapplicationbuilder builder = createspringapplicationbuilder();
    // 指定主类 bootstrap
    builder.main(getclass());
    // 回调 bootstrap 重写的方法
    builder = configure(builder);
    return run(builder.build());
}

这个方法会配置当前 web 应用程序上下文环境:指定主类、注册 servletcontext、调用 configure()、run 运行。

由于 bootstrap 重写了 configure(),所以会执行重写的方法来指定主类,最后通过 run 来完成启动 springboot 应用。