背景

首先说一下实践项目的形式:对外提供的API服务,无界面,无web.xml,打包方式:

<packaging>war</packaging>

问题说明

项目打包后,部署至Tomcat的webapps目录下,启动Tomcat,发现启动成功后,打印日志(INFO级别,非ERROR):

Unregistering JMX-exposed beans on shutdown

查看Tomcat状态:

jps

发现Tomcat未运行。

解决方案

问题原因: Tomcat容器和内置Tomcat冲突,即jar包冲突。
查看jar包:

cd tomcat/webapps
find . -name *tomcat*

发现有依赖jar包:

tomcat-embed-core-8.5.29.jar
tomcat-annotations-api.jar
tomcat-embed-el-8.5.29.jar

tomcat-jdbc-8.5.29.jar
tomcat-juli-8.5.29.jar

通过eclipse的Dependency Hierarchy,搜索tomcat关键字,发现前三个jar包属于spring-boot-starter-tomcat,后面俩依赖属于tomcat-jdbc

解决如下:

<!--部署成war包时开启↓↓↓↓-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jdbc</artifactId>
    <scope>provided</scope>
</dependency>
<!--部署成war包时开启↑↑↑↑-->

Maven的Scope简介

mavenscope依赖范围的概念:
依赖范围就是用来控制依赖和三种classpath(编译classpath,测试classpath、运行classpath)的关系,Maven有如下几种依赖范围:

compile: 编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。典型的例子是spring-code在编译、测试和运行的时候都需要使用该依赖

test: 测试依赖范围。使用次依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此依赖。典型的例子是Jnuit,它只有在编译测试代码及运行测试的时候才需要

provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时候无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器以及提供,就不需要Maven重复地引入一遍。

问题详解

在开发和测试过程中,使用的都是内置tomcat容器(存在于spring-boot-starter-web),但是部署到Tomcat容器中,就会出现冲突报错。

1) 继承SpringBootServletInitializer,修改应用启动方式:

外部容器部署的话,就不能依赖于Application的main函数了,而是要以类似于web.xml文件配置的方式来启动Spring应用上下文,此时我们需要在启动类中继承SpringBootServletInitializer并实现configure方法

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 WebApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(WebApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}

这个类的作用与在web.xml中配置负责初始化Spring应用上下文的监听器作用类似,只不过在这里不需要编写额外的XML文件了。

2)移除对嵌入式Tomcat的依赖

<!--部署成war包时开启↓↓↓↓-->
<!--添加servlet-api的依赖-->
<dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
   <scope>provided</scope>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-tomcat</artifactId>
   <scope>provided</scope>
</dependency>
<dependency>
   <groupId>org.apache.tomcat</groupId>
   <artifactId>tomcat-jdbc</artifactId>
   <scope>provided</scope>
</dependency>
<!--部署成war包时开启↑↑↑↑-->

3)打包方式

<packaging>war</packaging>

4)修改编译设置
方法一

<build>
  <plugins>
    <plugin>
      <artifactId>maven-war-plugin</artifactId>
      <version>2.6</version>
      <configuration>
        <!--如果想在没有web.xml文件的情况下构建WAR,请设置为false。-->      
        <failOnMissingWebXml>false</failOnMissingWebXml>                        
      </configuration>
    </plugin>
  </plugins>
</build>

方法二:版本3.0.0的插件 web.xml不存在问题,所以可以通过升级插件来解决问题

<plugin>
  <artifactId>maven-war-plugin</artifactId>
  <version>3.0.0</version>
</plugin>

5)上下文路径
打成的包的名称应该和application.yml

server.context-path=/api

保持一致

<build>
    <finalName>test</finalName>
</build>

如果不一样发布到tomcat的webapps下上下文会变化

如果想了解SpringBoot是如何帮我们省去web.xml的话,可以查看文章:
https://www.jianshu.com/p/3c94d7e76998?utm_source=oschina-app

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐