Java Web 学习笔记之十六:嵌入式web服务器Tomcat的基本使用

简介

Tomcat服务器是一个开放源码的轻量级Web应用服务器,非常适合搭建微服务应用。

Embedded Tomcat

嵌入式Tomcat服务器则无需部署外置tomcat,开发者只需引入嵌入式tomcat依赖,编写少量启动代码即可运行Web应用,是搭建微服务应用的首选方式之一。

使用

新建maven工程,引入依赖

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
<?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.github.johnsonmoon</groupId>
<artifactId>tomcat-embedded-test</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<tomcat.version>9.0.13</tomcat.version>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
</dependencies>
</project>

程序代码写法

编写启动代码

简单的程序入口代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) throws Exception {
String contextPath = "";
Path tempBaseDir = Files.createTempDirectory("tomcat-temp-base-dir");//创建临时目录作为tomcat的基础目录
Path tempDocDir = Files.createTempDirectory("tomcat-temp-doc-dir");//创建临时目录作为应用文档资源的目录

Tomcat tomcat = new Tomcat();
Connector connector = new Connector();
connector.setPort(8080);//设置绑定端口
tomcat.getService().addConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
tomcat.setBaseDir(tempBaseDir.toFile().getAbsolutePath());

StandardContext context = (StandardContext) tomcat.addWebapp(contextPath, tempDocDir.toFile().getAbsolutePath());//创建应用上下文
context.setParentClassLoader(Main.class.getClassLoader());
context.setUseRelativeRedirects(false);

//tomcat 启动jar扫描设置为跳过所有,避免与框架结合出现 jar file not found exception
System.setProperty("tomcat.util.scan.StandardJarScanFilter.jarsToSkip", "\\,*");

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

添加应用初始参数

通过 org.apache.catalina.core.StandardContext#addParameter 方法添加初始参数:

1
2
3
4
private static void configureContextParameters(StandardContext context) {
context.addParameter("param1", "1");
context.addParameter("param2", "2");
}

配置应用资源集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void configureResources(StandardContext context) {
File classesDir = new File(WORK_HOME, "target/classes");
File jarDir = new File(WORK_HOME, "lib");
WebResourceRoot resources = new StandardRoot(context);
if (classesDir.exists()) {
resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", classesDir.getAbsolutePath(), "/"));
System.out.println("Resources added: [classes]");
} else if (jarDir.exists()) {
resources.addJarResources(new DirResourceSet(resources, "/WEB-INF/lib", jarDir.getAbsolutePath(), "/"));
System.out.println("Resources added: [jar]");
} else {
resources.addPreResources(new EmptyResourceSet(resources));
System.out.println("Resources added: [empty]");
}
context.setResources(resources);
}

说明:

  • 其中的变量 WORK_HOME 为程序工作目录,即程序根目录 System.getProperty(“user.dir”)
  • 若存在 target/classes 目录则认为程序在IDE环境中启动,将classes目录下的资源作为PreResources添加至上下文中。
  • 若存在 lib 目录则认为是程序在构建打包之后,通过脚本启动的,则将lib目录下的资源作为JarResources添加至上下文中, 当然实际项目的依赖目录不一定是’lib’目录,可以根据实际情况修改。
  • 应用资源集合是非必须配置项,如果没有配置,那么在程序中使用注解定义的Servlet等类型就无法通过Tomcat自动加载。

添加 Servlets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void configureServlets(String contextPath, Tomcat tomcat, StandardContext context) {
// -- Writing1:
{
tomcat.addServlet(contextPath, "test1", TestServlet.class.getName());
context.addServletMappingDecoded("/test1/*", "test1");//注意不要忘记设置Servlet路径映射
}

// -- Writing2:
{
Wrapper wrapper = context.createWrapper();
wrapper.setName("test2");
wrapper.setServletClass(TestServlet2.class.getName());
context.addChild(wrapper);
context.addServletMappingDecoded("/test2/*", "test2");//注意不要忘记设置Servlet路径映射
}
}

说明:

上述程序添加Servlet有三种方式

  1. 通过 org.apache.catalina.startup.Tomcat#addServlet 方法添加
  2. 通过 org.apache.catalina.Wrapper 对象进行配置,并添加到上下文中
  3. 实现 Servlet 类时候使用注解 javax.servlet.annotation.WebServlet 进行修饰,通过应用启动后Tomcat容器自动扫描添加
其中第一、二种方式添加的Servlet代码示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.github.johnsonmoon.tomcat.embedded.test.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* Create by johnsonmoon at 2018/12/14 11:31.
*/
public class TestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello, world! -1-");
}
}
其中第三种方式添加的Servlet代码示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.github.johnsonmoon.tomcat.embedded.test.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* Create by johnsonmoon at 2018/12/14 11:32.
*/
@WebServlet(name = "test3", urlPatterns = {"/test3/*"})
public class TestServlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello, world! -3-");
}
}

并且需要保证配置的资源集中包含有该类型(classes资源集合 或 lib资源集合)

添加 Filters

1
2
3
4
5
6
7
8
9
10
11
private static void configureFilters(StandardContext context) {
FilterDef filterDef = new FilterDef();//Filter定义
filterDef.setFilterName("test-filter");
filterDef.setFilterClass(TestFilter.class.getName());
filterDef.addInitParameter("name", "test-filter");
context.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap();//Filter路径映射
filterMap.setFilterName("test-filter");
filterMap.addURLPattern("/*");
context.addFilterMap(filterMap);
}
其中添加的Filter代码如下
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
package com.github.johnsonmoon.tomcat.embedded.test.filter;

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

/**
* Create by johnsonmoon at 2018/12/14 16:38.
*/
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Enumeration<String> enumeration = filterConfig.getInitParameterNames();
if (enumeration.hasMoreElements()) {
String name = enumeration.nextElement();
System.out.println(String.format("Filter init: init-param name:[%s], value:[%s]", name, filterConfig.getInitParameter(name)));
}
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println(String.format("Filter doFilter: remote host: [%s]", request.getRemoteHost()));
chain.doFilter(request, response);
}

@Override
public void destroy() {

}
}

添加 Listeners

1
2
3
private static void configureListeners(StandardContext context) {
context.addApplicationEventListener(new TestListener());
}

启动、测试结果

代码启动后,可以通过 http://127.0.0.1:8080/test1 方式访问Servlet1的服务,并测试过滤器Filter的过滤情况。

浏览器响应如下:
image

程序日志输出如下:
image