Spring Boot应用程序部署到Tomcat的War包

78

我正在尝试将一个Spring Boot应用部署到Tomcat,因为我想要部署到AWS。 我创建了一个WAR文件,但它似乎无法在Tomcat上运行,尽管它是可见的。

详情:
0. 这是我的应用:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class App {
    public static void main(String[] args) {
        SpringApplication.run(SampleController.class, args);
    }
}

@Controller
@EnableAutoConfiguration
public class SampleController {
    @RequestMapping("/help")
    @ResponseBody
    String home() {
        String input = "Hi! Please use 'tag','check' and 'close' resources.";
        return input;
    }
}

application.properties 包含以下内容:

server.port=${port:7777}
  1. After reading a number of pages and question-answers I added following to my POM:

    http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0

    <groupId>com.niewlabs</groupId>
    <artifactId>highlighter</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <packaging>war</packaging>
    
    <properties>
        <java.version>1.8</java.version>
    </properties>    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    

  2. I ran"mvn package"and got WAR file (size 250Mb), which I put into "webapps" folder.

  3. I started Tomcat and am able to see my app listed, in my case "/highlighter-1.0-SNAPSHOT".
  4. Clicking on the link for the app results in "Status 404" page.
  5. When I run Spring Boot app just by itself, without container it runs on localhost:7777, but there is nothing there when I run it in Tomcat.
更新: 还有另一个参考。不确定它有多少用处。

4
你是否扩展了SpringBootServletInitializer类并重写了它的configure方法? - Andy Wilkinson
不,我在Spring Guide WAR指南中没有看到任何提及。您能否给我一个链接或详细信息? - Daniil Shevelev
2
@AndyWilkinson,感谢您的提示。我在Spring指南[http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-convert-an-existing-application-to-spring-boot]中找到了答案。但是我的应用程序仍然无法在Tomcat上运行。 - Daniil Shevelev
我按照文档中的所有步骤进行了操作,但仍然出现404错误。从Tomcat本地日志中可以看出应用程序已被检测到。09-Nov-2019 11:19:59.676 INFO [main] org.apache.catalina.core.ApplicationContext.log 1 Spring WebApplicationInitializers detected on classpath 09-Nov-2019 11:20:12.722 INFO [main] org.apache.catalina.core.ApplicationContext.log ContextListener: contextInitialized() - Ajay Yadav
@AjayYadav 你是怎么解决的? - Demobilizer
13个回答

110
本指南详细介绍了如何在Tomcat上部署Spring Boot应用程序:
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file 基本上,我需要添加以下类:
public class WebInitializer extends SpringBootServletInitializer {   
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(App.class);
    }    
}

我还向POM文件中添加了以下属性:

<properties>        
    <start-class>mypackage.App</start-class>
</properties>

7
我会更新参考链接为以下链接 - http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file “将jar转换为war maven”部分只在您提供的参考链接中指出了嵌入式版本。 无论如何,谢谢。 - nickolay.laptev
这个修复方法对我有效!我可以确认文档已按上面评论中建议的进行了更新。http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file - Miguel Reyes
2
我不确定是否需要添加<start-class>属性。在我的配置中不需要它,而且在参考文档中也没有提到它。 - LJ in NJ
由于应用程序在STS中运行时没有使用“ServeletInitializer”文件,我很自豪地删除了该文件,并头痛了两天,想知道为什么它不能在Tomcat上运行。感谢您的答案。通过查看这个Servlet名称,我记起了我的错误,在将该文件替换回来后,问题得到了解决。 - learner
我不需要使用<start-class>,因为我正在运行Spring Boot 2.1。 - Agustí Sánchez

27

嘿,确保按照这些更改更新pom.xml文件

<packaging>war</packaging>

在依赖项部分,请确保指定tomcat是提供的,这样您就不需要嵌入式的tomcat插件。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>       

    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>       

这是整个pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <start-class>com.example.Application</start-class>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>       

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>       

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

应用程序类应该是这样的

Application.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {


    /**
     * Used when run as JAR
     */
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    /**
     * Used when run as WAR
     */
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(Application.class);
    }

}

您可以添加一个控制器来测试MyController.java

package com.example;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

    @RequestMapping("/hi")
    public @ResponseBody String hiThere(){
        return "hello world!";
    }
}

接下来,您可以在Tomcat 8版本中运行该项目,并通过以下方式访问控制器:

http://localhost:8080/demo/hi

如果由于某些原因无法将项目添加到Tomcat中,请右键单击项目,然后转到Build Path->configure build path->Project Faces

确保仅选择了以下3项:

Dynamic web Module 3.1 Java 1.8 Javascript 1.0


22

我认为你对不同的编程范式有些混淆了。首先,war文件和服务器部署——这些属于Java企业版(Java EE)。这些概念在spring-boot应用程序中没有真正的位置,因为它遵循不同的模型。

Spring-boot负责创建嵌入式容器,并直接从标准jar文件中运行您的服务(尽管它可以做更多)。我认为这个模型的意图是支持微服务开发——每个服务都有自己的容器,是完全自包含的。您也可以使用您的代码生成Java EE应用程序,但考虑到spring-boot更容易(对于某些类型的应用程序/服务),那将是愚蠢的。

所以,基于这些信息,现在您必须决定要遵循什么编程范式,并且只能遵循它。

Spring-boot是可执行的——您只需从命令行或使用您喜欢的IDE或maven或gradle(提示:maven是正确的答案)运行App类中的main方法即可。这将启动一个tomcat服务器(默认情况下),并使您的服务在其中可用。根据您上面发布的配置,您的服务应该在以下URL中提供:http://localhost:7777/context/help——context应该替换为您的上下文名称,但您尚未共享它。

您不应该创建war文件、运行tomcat或部署任何内容。在spring-boot中,这些都是不必要的。您的pom中的打包应该是jar,而不是war,并且应该删除spring-boot-starter-tomcatscope——它肯定不是“provided”。

当您运行主方法时,控制台输出应告诉您已注册的上下文;使用它来正确获取URL。

话虽如此,就目前而言(直到被广泛采用之前),Spring Boot必须存在于JEE世界中。因此,Spring团队记录了一种构建war文件而不是可执行jar文件的方法,以便将其部署到servlet或JEE容器中。这使得Spring Boot技术可以在只允许使用war(或ear)的环境中使用。然而,这只是对这些环境非常普遍的事实做出的回应,并不被视为解决方案的必要甚至是可取的一部分。


5
我非常希望避免部署到Tomcat上,但似乎这是将我的应用程序部署到AWS最简单和最简便的方法。 - Daniil Shevelev
2
不要犹豫,使用spring-boot和docker是最简单的部署方式,包括AWS(相信我,我说的是经验之谈)。 - Software Engineer
2
你可以看一下这个链接:http://blog.adaofeliz.com/2014/11/21/first-look-spring-boot-and-docker/ - Software Engineer
4
我认为这并不完全正确。Spring Boot 允许将 WAR 文件部署到容器中。但对于大多数用例,原文是正确的。所以我很想同时点赞和踩问题。请参阅https://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html。 - Wes
2
如果您已经有一堆现有的经典Spring应用程序在Tomcat上运行,那么Spring Boot WAR文件部署是一种很好的方式,可以在不一次性进行太多更改的情况下开始使用Spring Boot。 - jkerak
显示剩余5条评论

6

您的Application.java类应该扩展SpringBootServletInitializer类 例如:

public class Application extends SpringBootServletInitializer {}

6

在按照指南(或使用Spring Initializr)后,我有一个在本地计算机上运行正常的WAR文件,但在远程服务器(Tomcat上运行)上无法工作。

没有错误信息,它只是说“找到了Spring servlet initializer”,但根本没有任何操作。

17-Aug-2016 16:58:13.552 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/8.5.4
17-Aug-2016 16:58:13.593 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive /opt/tomcat/webapps/ROOT.war
17-Aug-2016 16:58:16.243 INFO [localhost-startStop-1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.

并且

17-Aug-2016 16:58:16.301 INFO [localhost-startStop-1] org.apache.catalina.core.ApplicationContext.log 2 Spring WebApplicationInitializers detected on classpath
17-Aug-2016 16:58:21.471 INFO [localhost-startStop-1] org.apache.catalina.core.ApplicationContext.log Initializing Spring embedded WebApplicationContext
17-Aug-2016 16:58:25.133 INFO [localhost-startStop-1] org.apache.catalina.core.ApplicationContext.log ContextListener: contextInitialized()
17-Aug-2016 16:58:25.133 INFO [localhost-startStop-1] org.apache.catalina.core.ApplicationContext.log SessionListener: contextInitialized()

没有发生其他事情。Spring Boot只是没有运行。

显然,我使用Java 1.8编译了服务器,而远程计算机只有Java 1.7。

使用Java 1.7编译后,它开始工作了。

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.7</java.version> <!-- added this line -->
    <start-class>myapp.SpringApplication</start-class>
</properties>

4

使用Gradle的解决方案

build.gradle中添加插件

apply plugin: 'war'

提供的依赖项添加到Tomcat中。
dependencies {
    // other dependencies
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}

3

我曾经也遇到过同样的问题,后来按照这篇指南的步骤解决了它。我使用maven运行了以下目标:

clean package

这对我起作用了,谢谢。


3

公共类Application继承SpringBootServletInitializer {}。

仅需继承SpringBootServletInitializer便可在您的AWS/tomcat中运行。


1
你能否澄清一下,你的回答带来了什么之前在其他回答中没有提到的内容,无论是在你之前三个小时发布的回答中还是2015年被接受的回答中? - Foon

2
TL;DR: 确保您的应用程序依赖项和外部Tomcat安装具有相同的Tomcat版本和Java版本。
我按照所有步骤来创建一个生成WAR文件与外部Tomcat服务器兼容的Spring Boot应用程序。我也扩展了SpringBootServletInitializer。如果您还没有这样做,请首先根据其他答案或官方Spring文档中传统部署的说明进行更改。
该应用程序在STS嵌入式Tomcat上运行良好,但在外部Tomcat上会出现404 - Not Found错误。没有错误并且catalina.bat run命令也显示“部署war完成”的信息。
14-Apr-2021 12:38:01.996 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
14-Apr-2021 12:38:01.996 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/10.0.5]
14-Apr-2021 12:38:02.023 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [C:\dev\apache-tomcat-10.0.5\webapps\server-test-1-0.0.1-SNAPSHOT.war]
14-Apr-2021 12:38:05.349 INFO [main] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
14-Apr-2021 12:38:05.436 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [C:\dev\apache-tomcat-10.0.5\webapps\server-test-1-0.0.1-SNAPSHOT.war] has finished in [3,413] ms

通常情况下,与SpringBoot相关的输出(例如“Initializing Spring DispatcherServlet 'dispatcherServlet'”)也应该出现在Catalina控制台上。但是它没有出现。
对我来说,问题是: 我在我的Maven依赖文件夹中有tomcat-embed-core-9.0.44。但是我在我的机器上安装了一个独立的Tomcat版本10.x.xx。
我在我的机器上安装了一个Tomcat 9.0.xx版本,应用程序在其中运行良好。

1

2018年2月3日更新,使用Spring Boot 1.5.8.RELEASE版本。

在pom.xml中,您需要将打包方式改为war文件,并告诉Spring插件在构建时使用,如下所示:

<packaging>war</packaging>

此外,在构建包时,您需要通过添加以下内容来排除嵌入式Tomcat:
    <!-- to deploy as a war in tomcat -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>

完整的可运行示例在这里 https://www.surasint.com/spring-boot-create-war-for-tomcat/


3
这还不够。主要的Application类必须实现SpringBootServletInitializer,以便适用于容器和独立执行。Op的示例缺少它。 - Aram Paronikyan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接