部署Spring Boot项目到Tomcat服务器

修改应用启动类

使得Application继承SpringBootServletInitializer协议,并重写configure方法,如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Application extends SpringBootServletInitializer {

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

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

public void run(String... args) throws Exception {
LogUtils.info("服务启动完成!");
}
}

修改包配置

1
2
3
4
<groupId>com.luci.beefun</groupId>
<artifactId>beefun</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>

剔除Tomcat依赖

本文采用该Spring官方提供的方式

1
2
3
4
5
6
7
8
9
10
<!--参考:https://docs.spring.io/spring-boot/docs/current/reference/html/build-tool-plugins-maven-plugin.html-->
<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>

如何验证是否已经剔除,可以在war包解压后(在部署后会自动解压)lib目录看是否有内置的tomcat相关包。可以看到tomcat相关包都在lib-provided目录下。

打包

先clean,然后在package,之后再target目录下就可以找打对应的文件。

默认8080端口部署

将war包,上传到webapp目录下,启动tomcat,tomcat会自动解压该文件:

之后,通过访问:

http://localhost:8080/war_package_name/api_name

war_package_name:包名

api_name:接口名

比如,

http://localhost:8080/beefun-0.1.0/hello

多端口部署

假如要实现多端口部署需要做以下:

server.xml 增加多端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- BeeFun -->
<Service name="BeeFun">
<Connector port="8082" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="BeeFun" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="beefunapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%t [%I] %{X-Forwarded-For}i %a %r %s %D" />
</Host>
</Engine>
</Service>

注意修改的点:

  1. 其中name,自定义即可。

  2. 该端口号,即为自定义端口号,此处定义为8082。

  3. 同时修改AJP/1.3端口号,不要与以上重复。该接口用于向其它服务器做转发,比如Nginx。

  4. 修改Engine name,自定义,最好与Service一致。

  5. appBase此处为放置war包的路径,类似于webapps目录。如下图所示:

war包

最后,将beefun.war文件放于beefun目录即可,同时通过以下地址访问:

访问

http://localhost:8082/beefun/tags

如果放置到服务器,就对应是:http://12.34.56.78:8082/beefun/tags

错误

JMX错误

场景: 同样的项目,同样的代码,如果tomcat只部署一个项目,就没问题。但是部署两个问题,其中有一个tomcat就会无法启动,也就无法访问。错误信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start: 
org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[BeeFun].StandardHost[localhost].StandardContext[/beefun1]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:754)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:730)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:985)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1857)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [CachingConnectionFactory [channelCacheSize=25, host=localhost, port=5672, active=true rabbitConnectionFactory]] with key 'rabbitConnectionFactory'; nested exception is javax.management.InstanceAlreadyExistsException: org.springframework.amqp.rabbit.connection:name=rabbitConnectionFactory,type=CachingConnectionFactory
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:628)
at org.springframework.jmx.export.MBeanExporter.registerBeans(MBeanExporter.java:550)
at org.springframework.jmx.export.MBeanExporter.afterSingletonsInstantiated(MBeanExporter.java:432)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:781)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
at org.springframework.boot.web.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:154)
at org.springframework.boot.web.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:134)
at org.springframework.boot.web.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:87)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5245)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 10 more
Caused by: javax.management.InstanceAlreadyExistsException: org.springframework.amqp.rabbit.connection:name=rabbitConnectionFactory,type=CachingConnectionFactory
at com.sun.jmx.mbeanserver.Repository.addMBean(Repository.java:437)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerWithRepository(DefaultMBeanServerInterceptor.java:1898)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerDynamicMBean(DefaultMBeanServerInterceptor.java:966)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerObject(DefaultMBeanServerInterceptor.java:900)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerMBean(DefaultMBeanServerInterceptor.java:324)
at com.sun.jmx.mbeanserver.JmxMBeanServer.registerMBean(JmxMBeanServer.java:522)
at org.springframework.jmx.support.MBeanRegistrationSupport.doRegister(MBeanRegistrationSupport.java:195)
at org.springframework.jmx.export.MBeanExporter.registerBeanInstance(MBeanExporter.java:682)
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:618)
... 25 more

06-Jun-2018 00:05:47.521 严重 [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Error deploying web application archive [/Users/wenghengcong/Open/apache-tomcat-8.5.31/beefunapps/beefun1.war]
java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[BeeFun].StandardHost[localhost].StandardContext[/beefun1]]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:758)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:730)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:985)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1857)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

在网上找了无数答案,就两种说法比较多:

  1. Tomcat 配置问题
  2. jar包冲突:特指servlet-api.jar。

最后直接Google搜索:

org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean

才找到问题的根本原因:

jmx冲突!!!

解决方案就是,在项目配置文件里加上:

由于我的配置是yml格式如下:

1
2
3
4
spring:
#重点
jmx:
default-domain: beefun

此时,只要将两个包对应的default-domain设置成不一样的值就行,保证Tomcat全局唯一!

Tomcat 依赖包

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.luci.beefun.appserver.Application]; nested exception is java.lang.IllegalStateException: Failed to introspect annotated methods on class org.springframework.boot.web.support.SpringBootServletInitializer

在开发时,Debug Application方式启动,在pom中把tomcat的scope改为compile

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>compile</scope>
</dependency>

注意,打包是,要修改为:

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

数据库相关错误

数据库连接不上

Cannot determine embedded database driver class for database type NONE
Ask Question

可以通过删除数据库,重建数据库。

matches criteria [is assignable to Object]

假如使用druid时,启动程序后,一直停留在下面这行:

1
com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited

那么就需要检查下配置:

1
2
mybatis:
type-aliases-package: com.***.**.mybatis.database

对应的数据库Model类文件路径是否正确,如下图我的配置如上,文件夹路径如下:

屏幕快照 2018-06-16 11.46.52

SpringBootApplication

SpringBootApplication 配置scanBasePackages,需要将Controller、Dao路径导入。

1
@SpringBootApplication(scanBasePackages = {"com.luci.beefun.controller","com.luci.beefun.mybatis.service"})

或者可以全路径:

1
@SpringBootApplication(scanBasePackages = "com.luci.beefun.*")

#参考

把spring-boot项目部署到外部tomcat环境下