使用 Embed Tomcat 嵌入式启动 JavaWeb 项目

之前写过一篇文章讲 在 maven 项目中使用 tomcat7-maven-plugin 插件启动 JavaWeb 项目,这种方式有个不好的地方在于每次启动 tomcat7-maven-plugin 插件都会执行编译打包,比较费时,特别是大型项目。其实这在 IDE 开发环境中是多余的,因为 IDE 本来就会跟我们自动编译修改好的文件。

本文就带大家尝试另外一种启动方式,使用 Embed Tomcat 在应用中嵌入式启动 JavaWeb 项目(SpringBoot 能使用 main 方法启动 web 项目用的也是这种机制)。

1,首先创建一个 maven 的 web 项目,如下图:

1583924601392

1583924651228

2,添加相关目录和一些测试文件(类),最后项目结构如下:

1583979423600

各文件内容:

pom.xml

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
<?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.zhangzw</groupId>
<artifactId>embed</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<!-- 添加 embed tomcat 依赖 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.34</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

</project>

demo.js

1
alert("demo"); // 测试 JS 文件

DemoServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.zhangzw.servlet;

import java.io.IOException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 一个简单的 Servlet
public class DemoServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getOutputStream().write("Hello".getBytes());
}
}

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">

<!-- DemoServlet 配置 -->
<servlet>
<servlet-name>demoServlet</servlet-name>
<servlet-class>com.zhangzw.servlet.DemoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demoServlet</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>

</web-app>

EmbedStarter.java

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
package com.zhangzw;

import java.io.File;

import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;

// Tomcat 启动类
public class EmbedStarter {

public static void main(String[] args) throws Exception {
String userDir = System.getProperty("user.dir"); // 项目目录
String tomcatBaseDir = userDir + File.separatorChar + "tomcat";
String webappDir = userDir + File.separatorChar + "target" + File.separatorChar + "classes";

Tomcat tomcat = new Tomcat();
tomcat.setBaseDir(tomcatBaseDir);

Connector connector = new Connector();
connector.setPort(9999); // 端口号
connector.setURIEncoding("UTF-8");
tomcat.getService().addConnector(connector);

tomcat.addWebapp("/", webappDir);

tomcat.start();
tomcat.getServer().await();
}
}

EmbedStarter 类就是我们嵌入式启动 Tomcat 的入口,执行 main 方法就启动当前 Web 项目了。代码很简单,需要说明的是里面几个目录的含义:

  • userDir:项目目录,用于其它目录定位相对路径。
  • tomcatBaseDir:Tomcat 应用存放的目录,这个放哪儿没关系。
  • webappDir:项目部署目录,我们这里需要设置为 $userDir$/target/classes 目录,因为项目编译的文件都会存到改目录下。

需要注意的是:在 IDEA 中需要在项目架构中,将 webapp 目录作为 Resources 资源目录,这样 webapp 下的文件才会随着编译到 target/classes 目录下,Tomcat 才能读的到。

1583979910599

3,执行 EmbedStarter.main 方法,启动 Tomcat,能打印如下日志表示启动成功:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
三月 12, 2020 10:40:31 上午 org.apache.catalina.core.StandardContext setPath
警告: A context path must either be an empty string or start with a '/' and do not end with a '/'. The path [/] does not meet these criteria and has been changed to []
三月 12, 2020 10:40:31 上午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-9999"]
三月 12, 2020 10:40:32 上午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
信息: Using a shared selector for servlet write/read
三月 12, 2020 10:40:32 上午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Tomcat]
三月 12, 2020 10:40:32 上午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet Engine: Apache Tomcat/8.5.34
三月 12, 2020 10:40:32 上午 org.apache.catalina.startup.ContextConfig getDefaultWebXmlFragment
信息: No global web.xml found
三月 12, 2020 10:40:37 上午 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.
三月 12, 2020 10:40:37 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
警告: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [314] milliseconds.
三月 12, 2020 10:40:37 上午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-9999"]

然后访问 DemoServlet 和 demo.js,都能正常返回:

1583980965096

1583980988761