如何将War文件部署到Spring Boot嵌入式的Tomcat中 - Vojtech Ruzicka


默认情况下,Spring Boot在嵌入式Tomcat中以jar形式运行(尽管您可以将其部署为常规WAR)。与嵌入式应用程序服务器一起运行非常好,但是有时您可能需要将另一场战争与嵌入式应用程序捆绑在一起。也许您有一个旧版应用程序需要包括在内,但不想为此设置常规的Tomcat。
幸运的是,使用Spring Boot,这很容易。具体实现方式取决于您的Spring Boot版本。

Spring Boot 2.x
您需要做的就是在您的一个@Configurationtype类中声明一个bean TomcatServletWebServerFactory。然后,您需要重写getTomcatWebServer()方法。这样增加war如同运行tomcat.addWebApp()一样简单。
请注意,在下面的示例中,webapps由于默认情况下该目录不存在,因此需要创建该目录。它是通常将war部署到Tomcat的目录。

@Bean
public TomcatServletWebServerFactory servletContainerFactory() {
    return new TomcatServletWebServerFactory() {
    
      @Override
      protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
          // webapps directory does not exist by default, needs to be created
          new File(tomcat.getServer().getCatalinaBase(),
"webapps").mkdirs();
    
         
// Add a war with given context path
         
// Can add multiple wars this way with different context paths
          tomcat.addWebapp(
"context-path", "path-to-your-war.war");
    
          return super.getTomcatWebServer(tomcat);
      }
    
    };
}

您可以将它们外部化为属性,而不必硬编码WAR的路径及其上下文路径,并且仅在存在这些属性时使用加载WAR(使用@ConditionalOnProperty)。
您可以将它们放入您application.properties的应用程序中,也可以在运行应用程序时将它们作为命令行参数传递。这样,您可以例如在进行本地开发时跳过加载您的WAR,或者在不同的环境中提供不同的工件。

@Bean
@ConditionalOnProperty(name = "external.war.file")
public TomcatServletWebServerFactory servletContainerFactory(@Value(
"${external.war.file}") String path,
                                                             @Value(
"${external.war.context}") String contextPath) {
    return new TomcatServletWebServerFactory() {

        @Override
        protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
            new File(tomcat.getServer().getCatalinaBase(),
"webapps").mkdirs();

            tomcat.addWebapp(contextPath, path);

            return super.getTomcatWebServer(tomcat);
        }

    };
}

如果要将WAR部署到/,则需要使用一个空字符串作为上下文根。为了避免与Spring Boot应用程序发生冲突,可以application.properties通过设置来更改其上下文根server.servlet.context-path。

使用胖JAR中的依赖项
如果要避免两个工件都使用重复的依赖关系,则可以指定外部WAR应使用胖JAR的类加载器。

Context context = tomcat.addWebapp("context-path", "path-to-your-war.war");
context.setParentClassLoader(getClass().getClassLoader());

Spring Boot 1.x
Spring Boot 2.x进行了大量的重构,在运行Spring Boot 1.x时,您需要使用其他类来部署WAR。还请注意该try-catch块,这对于捕获方法抛出的已检查异常是必需的。

@Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {

            new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs();
            
            try {
                tomcat.addWebapp(
"context-path", "path-to-your-war.war");
            } catch (ServletException e) {
                log.error(
"Unable to deploy war to embedded Tomcat");
            }

            return super.getTomcatEmbeddedServletContainer(tomcat);
        }

    };
}

如果您想更改Spring Boot应用程序的上下文路径,则该过程与2.x相同,除了其中的属性名称application.properties不同,这也适用于1.x版本server.context-path。

添加对JSP的支持
如果您的外部非Spring Boot WAR包含JSP,则需要确保为其提供适当的依赖项,因为嵌入式Tomcat默认情况下不包含它们。对于Maven,您可以使用:

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>

对于Gradle,请改用以下代码:

compile "org.apache.tomcat.embed:tomcat-embed-jasper"
compile
"javax.servlet:jstl"