Spring Boot:如何将另一个WAR文件添加到嵌入式tomcat?

zeo*_*dtr 28 spring tomcat spring-boot

Spring Boot的嵌入式tomcat非常方便,适用于开发和部署.

但是如果应该添加另一个(第三方)WAR文件(例如,GeoServer)呢?

也许以下是正常程序:

  1. 安装普通的Tomcat服务器.
  2. 将Spring Boot应用程序构建为WAR文件,并将其添加到Tomcat的webapps文件夹中.
  3. 还要将另一个(第三方)WAR文件添加到webapps文件夹.

但如果可以进行以下配置,那就太好了.

  1. 将Spring启动应用程序构建为独立的Jar,其中包括嵌入式Tomcat.
  2. 部署Spring启动应用程序Jar.
  3. 将另一个(第三方)WAR文件添加到嵌入式Tomcat识别的文件夹中.
  4. 使用嵌入式Tomcat提供Spring引导应用程序内容和另一个WAR的内容.

怎么做到呢?

UPDATE

当spring引导应用程序由胖jar(=可执行jar)构成时,答案中的代码是不够的.修订后的内容如下:

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

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            try {
                Context context = tomcat.addWebapp("/foo", "/path/to/foo.war");
                WebappLoader loader =
                    new WebappLoader(Thread.currentThread().getContextClassLoader());
                context.setLoader(loader);
            } catch (ServletException ex) {
                throw new IllegalStateException("Failed to add webapp", ex);
            }
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }

    };
}
Run Code Online (Sandbox Code Playgroud)

由于系统类加载器无法加载胖jar中的jar文件,因此必须指定显式父类加载器.否则,附加WAR无法将库jar加载到添加了WAR的spring boot应用程序的fat jar中.

And*_*son 26

您可以使用添加war文件到嵌入式Tomcat Tomcat.addWebapp.正如其javadoc所说,它"相当于向Tomcat的web应用程序目录添加Web应用程序".要在Spring Boot中使用此API,您需要使用自定义TomcatEmbeddedServletContainerFactory子类:

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

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            // Ensure that the webapps directory exists
            new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs();

            try {
                Context context = tomcat.addWebapp("/foo", "/path/to/foo.war");
                // Allow the webapp to load classes from your fat jar
                context.setParentClassLoader(getClass().getClassLoader());
            } catch (ServletException ex) {
                throw new IllegalStateException("Failed to add webapp", ex);
            }
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }

    };
}
Run Code Online (Sandbox Code Playgroud)

  • 一个小问题:当application.properties中未指定server.tomcat.basedir时,WAR扩展失败.原因如下:如果未指定该变量,Spring将创建一个临时目录并将其指定为baseDir.但是该目录没有'wepapps'子目录,其中嵌入式tomcat尝试将mkdir()作为WAR扩展文件的子目录. (6认同)
  • 在Spring Boot 2中,已将“ TomcatEmbeddedServletContainerFactory”替换为:/sf/ask/3339008081/ (2认同)

Voj*_*cka 6

接受的答案涵盖 Spring Boot 1.x。提到的类不再出现在 Spring Boot 2.x 中。使用版本 2 时,您需要使用不同的版本:

    @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();

                Context context = tomcat.addWebapp(contextPath, path);
                context.setParentClassLoader(getClass().getClassLoader());

                return super.getTomcatWebServer(tomcat);
            }

        };
    }
Run Code Online (Sandbox Code Playgroud)

此外,Spring Boot 嵌入式 Tomcat 默认不包含 JSP 的依赖项。如果您在外部战争中使用 JSP,则需要包含它们。

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

更新:我写了一篇更详细的博客文章,介绍如何为 Spring Boot 1 和 2 进行设置