Springboot项目通常打包为jar,直接就可以运行,还是比较方便的。但是最近项目需要将springboot项目打包成war,放到单独的tomcat中进行运行,还是有好些需要注意的地方。

1、继承SpringBootServletInitializer

外部容器部署,此时需要在启动类中继承SpringBootServletInitializer并实现configure方法,并且在application.sources方法传入启动类。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 
@SpringBootApplication
public class WarDemoApplication extends SpringBootServletInitializer {
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 
        return application.sources(WarDemoApplication.class);
    }
 
    public static void main(String[] args) { 
        SpringApplication.run(WarDemoApplication.class, args);
    }
 
}

 2、pom.xml修改tomcat相关的配置

如果要将最终的打包形式改为war的话,还需要对pom.xml文件进行修改,因为spring-boot-starter-web中包含内嵌的tomcat容器,所以直接部署在外部容器会冲突报错。

这里有两种方法可以解决,如下

方法一:移除Tomcat依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

在这里需要移除对嵌入式Tomcat的依赖,这样打出的war包中,在lib目录下才不会包含Tomcat相关的jar包。

还有一个很关键的关键点,就是tomcat-embed-jasper中scope必须是provided。

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

因为SpringBootServletInitializer需要依赖 javax.servlet,而tomcat-embed-jasper下面的tomcat-embed-core中就有这个javax.servlet,如果没用provided,最终打好的war里面会有servlet-api这个jar,这样就会跟tomcat本身的冲突了。

方法二:将Tomcat配置为provided

spring-boot-starter-tomcat是Spring Boot默认就会配置的,即Spring Boot的内嵌tomcat,将其设置为provided是在打包时会将该包(依赖)排除,因为要放到独立的tomcat中运行,Spring Boot内嵌的Tomcat是不需要用到的。
 

<!--部署成war包时开启【同时支持war和jar启动部署】↓↓↓↓-->
        <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>
        <!--部署成war包时开启【同时支持war和jar启动部署】↑↑↑↑-->

这种方式的好处是,打包的war包同时适合java -jar命令启动以及部署到外部容器中。

3、由jar变成war

<packaging>war</packaging>

4、注意的问题

4.1、项目名称和打包名称的一致性

使用外部Tomcat部署访问的时候,application.properties(或者application.yml)中配置的server.port和server.servlet.context-path将失效。

将使用tomcat的端口,tomcat,webapps下项目名进行访问。为了防止应用上下文所导致的项目访问资源加载不到的问题,建议pom.xml文件中<build></build>标签下添加<finalName></finalName>标签:

<build>
    <!-- 应与application.properties(或application.yml)中context-path保持一致 -->
    <finalName>war包名称</finalName>    
</build>

此时打成的包的名称应该和application.properties的 

server.port=8081
server.servlet.context-path=/war-demo

注意SpringBooot2.x,这里的写法为 server.servlet.context-path=

保持一致
 

<build>
    <finalName>war-demo</finalName>
</build>

4.3、Error assembling WAR: web.xml attribute is required
 Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.2:war (default-war)

on project XXX: Error assembling WAR: web.xml attribute is required (or pre-existing WEB-INF/web.xml if executing in update mode)

这个提示并不是你缺少依赖,而是在打包的时候,没有web.xml文件,因为SpringCloud 微服务多模块下没有web.xml文件,此时也不需要加入,在pom文件下加入一行代码如下:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
     <version>3.0.0</version>
     <configuration>
         <failOnMissingWebXml>false</failOnMissingWebXml>
         <warName>${project.artifactId}</warName>
     </configuration>
</plugin>

此时不需要使用jar包的打包了。

<!-- <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>2.1.1.RELEASE</version>
        <configuration>
             <fork>true</fork> 如果没有该配置,devtools不会生效
        </configuration>
        <executions>
            <execution>
               <goals>
                   <goal>repackage</goal>
               </goals>
            </execution>
        </executions>
     </plugin> -->