8.1 Tomcat 主要版本特性对比 (Tomcat 7, 8, 9, 10, 11) 8.1 Tomcat 主要版本特性对比 (Tomcat 7, 8, 9, 10, 11) 8.1.1 Tomcat 版本概览与发展脉络 在深入比较各版本特性之前,我们先简要回顾一下 Tomcat 的版本发展脉络,了解每个版本发布的大致时间以及其在 Tomcat 发展史上的地位。 Tomcat 7: 发布于 2011 年,是 Tomcat 6 的继任者,实现了 Servlet 3.0 和 JSP 2.2 规范。Tomcat 7 在稳定性、性能和易用性方面都有显著提升,是当时企业级应用的首选版本。 Tomcat 8: 发布于 2014 年,实现了 Servlet 3.1、JSP 2.
在深入比较各版本特性之前,我们先简要回顾一下 Tomcat 的版本发展脉络,了解每个版本发布的大致时间以及其在 Tomcat 发展史上的地位。
Tomcat 7: 发布于 2011 年,是 Tomcat 6 的继任者,实现了 Servlet 3.0 和 JSP 2.2 规范。Tomcat 7 在稳定性、性能和易用性方面都有显著提升,是当时企业级应用的首选版本。
Tomcat 8: 发布于 2014 年,实现了 Servlet 3.1、JSP 2.3 和 WebSocket 1.1 规范,并引入了对 HTTP/2 的支持。Tomcat 8 进一步提升了性能,并开始拥抱新的 Web 标准。
Tomcat 9: 发布于 2016 年,实现了 Servlet 4.0、JSP 2.3 和 WebSocket 1.1 规范,并继续增强对 HTTP/2 的支持。Tomcat 9 主要关注性能优化和对最新 Java 版本的支持。
Tomcat 10: 发布于 2020 年,是一个重要的里程碑版本,它迁移到了 Jakarta EE 平台,实现了 Jakarta Servlet 5.0 和 Jakarta Server Pages 3.0 规范。Tomcat 10 是第一个基于 Jakarta EE 的版本,标志着 Java EE 向 Jakarta EE 的过渡。需要注意的是,由于 Jakarta EE 命名空间的变更,从 javax.* 变更为 jakarta.*,Tomcat 10 与之前的版本在 API 上存在不兼容性。
Tomcat 11: 发布于 2022 年,是 Tomcat 10 的继任者,实现了 Jakarta Servlet 6.0 和 Jakarta Server Pages 4.0 规范。Tomcat 11 继续 Jakarta EE 的发展路线,并引入了更多的新特性和性能改进。
可以用 Mermaid 的 graph TD 图来可视化 Tomcat 的版本演进关系:
图 8.1.1 Tomcat 版本演进图
从图中可以看出,Tomcat 7, 8, 9 属于 Java EE 时代,而 Tomcat 10 和 11 则迈入了 Jakarta EE 时代。这个转变是理解 Tomcat 10 和 11 特性的关键。
接下来,我们将详细对比 Tomcat 7, 8, 9, 10 和 11 的主要特性,包括支持的规范版本、Java 版本要求、关键新特性以及性能改进等方面。
Tomcat 的每个版本都旨在实现最新的 Servlet 和 JSP 规范,以及其他相关的 Web 标准。下表总结了这五个版本支持的主要规范版本:
| 特性 | Tomcat 7 | Tomcat 8 | Tomcat 9 | Tomcat 10 | Tomcat 11 |
|---|---|---|---|---|---|
| Servlet API | 3.0 | 3.1 | 4.0 | Jakarta Servlet 5.0 | Jakarta Servlet 6.0 |
| JSP API | 2.2 | 2.3 | 2.3 | Jakarta Server Pages 3.0 | Jakarta Server Pages 4.0 |
| EL API | 2.2 | 3.0 | 3.0 | Jakarta Expression Language 4.0 | Jakarta Expression Language 5.0 |
| WebSocket API | - | 1.1 | 1.1 | Jakarta WebSocket 2.0 | Jakarta WebSocket 2.1 |
| HTTP/2 支持 | - | 支持 | 增强 | 支持 | 增强 |
| Java 版本要求 | 6+ | 7+ | 8+ | 8+ (推荐 11+) | 8+ (推荐 17+) |
| Jakarta EE 平台 | 否 | 否 | 否 | 是 | 是 |
关键点:
Servlet API 和 JSP API 的升级: 从 Tomcat 7 到 9,Servlet 和 JSP API 逐步升级,带来了新的功能和改进。从 Tomcat 10 开始,迁移到 Jakarta EE 平台,API 命名空间发生变化。
WebSocket 和 HTTP/2 支持: Tomcat 8 引入了 WebSocket 和 HTTP/2 的支持,Tomcat 9 和 11 在 HTTP/2 支持上进行了增强。
Jakarta EE 迁移: Tomcat 10 是一个分水岭,标志着从 Java EE 到 Jakarta EE 的迁移。Tomcat 10 和 11 完全基于 Jakarta EE 规范。
Java 版本要求: Tomcat 版本对 Java 版本的要求逐步提高,Tomcat 10 和 11 推荐使用 Java 11+ 和 17+,以获得最佳性能和兼容性。
Tomcat 7 作为 Tomcat 6 的继任者,主要关注于 Servlet 3.0 和 JSP 2.2 规范的实现,并带来了以下关键特性:
Servlet 3.0 规范:
注解驱动的 Servlet 开发: 引入了 @WebServlet, @WebFilter, @WebListener 等注解,简化了 Servlet 组件的配置,减少了 web.xml 的依赖。
Servlet 异步处理: 允许 Servlet 在处理请求时将耗时操作移交给异步线程,提高服务器的并发处理能力。
可插拔 Servlet: 允许将 Web 应用程序打包成 JAR 文件,并直接部署到 Tomcat 中,无需显式声明 Servlet。
文件上传 API 改进: 提供了更方便的文件上传 API,支持 multipart/form-data 请求的处理。
代码实践 - Servlet 3.0 注解:
在 Tomcat 7 中,我们可以使用注解来声明 Servlet,例如:
package com.example; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/hello") public class HelloServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("Hello, Tomcat 7!"); } }
详解:
@WebServlet("/hello") 注解将 HelloServlet 类声明为一个 Servlet,并将其映射到 URL 路径 /hello。
无需在 web.xml 中配置 Servlet 映射,Tomcat 7 会自动扫描并注册带有 @WebServlet 注解的 Servlet。
JSP 2.2 规范:
代码实践 - JSP 2.2 EL 静态方法调用:
在 JSP 2.2 中,我们可以使用 EL 表达式调用静态方法:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.lang.Math" %> <html> <head> <title>JSP 2.2 Example</title> </head> <body> <p>The square root of 2 is: ${Math.sqrt(2)}</p> </body> </html>
详解:
${Math.sqrt(2)} EL 表达式调用了 java.lang.Math 类的静态方法 sqrt()。
JSP 2.2 允许在 EL 表达式中直接调用静态方法,增强了 JSP 页面的灵活性。
总结: Tomcat 7 是一个成熟稳定的版本,Servlet 3.0 和 JSP 2.2 规范的引入极大地简化了 Web 应用程序的开发,提升了开发效率。
Tomcat 8 基于 Tomcat 7,实现了 Servlet 3.1、JSP 2.3、WebSocket 1.1 规范,并引入了对 HTTP/2 的支持,主要特性包括:
Servlet 3.1 规范:
非阻塞 IO: 引入了非阻塞 IO API,允许 Servlet 使用非阻塞方式读取请求和写入响应,进一步提升服务器的并发处理能力。
HTTP 升级机制: 支持 HTTP 升级机制,为 WebSocket 和 HTTP/2 等协议的升级提供了基础。
代码实践 - Servlet 3.1 非阻塞 IO:
package com.example; import java.io.IOException; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(urlPatterns = "/async-io", asyncSupported = true) public class AsyncIOServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { AsyncContext asyncContext = request.startAsync(); ServletInputStream inputStream = request.getInputStream(); ServletOutputStream outputStream = response.getOutputStream(); inputStream.setReadListener(new ReadListenerImpl(inputStream, outputStream, asyncContext)); } private static class ReadListenerImpl implements javax.servlet.ReadListener { private ServletInputStream inputStream; private ServletOutputStream outputStream; private AsyncContext asyncContext; public ReadListenerImpl(ServletInputStream inputStream, ServletOutputStream outputStream, AsyncContext asyncContext) { this.inputStream = inputStream; this.outputStream = outputStream; this.asyncContext = asyncContext; } @Override public void onDataAvailable() throws IOException { byte[] buffer = new byte[1024]; int len = -1; while (inputStream.isReady() && (len = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, len); } } @Override public void onAllDataRead() throws IOException { outputStream.flush(); asyncContext.complete(); } @Override public void onError(Throwable t) { asyncContext.complete(); t.printStackTrace(); } } }
详解:
@WebServlet(urlPatterns = "/async-io", asyncSupported = true) 注解声明 Servlet 支持异步处理。
request.startAsync() 启动异步上下文。
inputStream.setReadListener() 设置读取监听器,使用非阻塞方式读取请求数据。
ReadListenerImpl 实现了 javax.servlet.ReadListener 接口,处理读取事件,当数据可用时,onDataAvailable() 方法会被调用,非阻塞地读取数据并写入响应。
JSP 2.3 规范:
WebSocket 1.1 规范:
代码实践 - WebSocket 1.1:
package com.example; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; @ServerEndpoint("/websocket") public class WebSocketEndpoint { @OnOpen public void onOpen(Session session) { System.out.println("WebSocket opened: " + session.getId()); } @OnMessage public void onMessage(String message, Session session) throws IOException { System.out.println("Received message: " + message); session.getBasicRemote().sendText("Server received: " + message); } @OnClose public void onClose(Session session) { System.out.println("WebSocket closed: " + session.getId()); } @OnError public void onError(Session session, Throwable error) { System.err.println("WebSocket error: " + error.getMessage()); } }
详解:
@ServerEndpoint("/websocket") 注解将 WebSocketEndpoint 类声明为一个 WebSocket 端点,并映射到 URL 路径 /websocket。
@OnOpen, @OnMessage, @OnClose, @OnError 等注解分别处理 WebSocket 连接打开、接收消息、连接关闭和错误事件。
session.getBasicRemote().sendText() 方法用于向客户端发送文本消息。
HTTP/2 支持:
总结: Tomcat 8 在 Tomcat 7 的基础上,引入了 Servlet 3.1、WebSocket 1.1 和 HTTP/2 支持,进一步提升了服务器的性能和功能,为开发更现代化的 Web 应用提供了基础。
Tomcat 9 基于 Tomcat 8,实现了 Servlet 4.0 规范,并增强了对 HTTP/2 的支持,主要特性包括:
Servlet 4.0 规范:
HTTP/2 服务端推送 (Server Push): 允许服务器主动向客户端推送资源,提高页面加载速度。
HTTP/2 优先帧 (Priority Frames): 支持 HTTP/2 优先帧,允许客户端指示资源加载的优先级。
代码实践 - Servlet 4.0 HTTP/2 服务端推送:
package com.example; 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 javax.servlet.http.PushBuilder; import java.io.IOException; @WebServlet("/push") public class PushServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PushBuilder pushBuilder = request.newPushBuilder(); if (pushBuilder != null) { pushBuilder.path("styles.css").push(); pushBuilder.path("script.js").push(); } response.getWriter().println("<html><head><link rel='stylesheet' href='styles.css'></head><body><h1>Hello HTTP/2 Push!</h1><script src='script.js'></script></body></html>"); } }
详解:
request.newPushBuilder() 获取 PushBuilder 对象,用于构建推送请求。
pushBuilder.path("styles.css").push() 和 pushBuilder.path("script.js").push() 分别推送 styles.css 和 script.js 资源。
当客户端请求 /push Servlet 时,服务器会同时推送 styles.css 和 script.js 资源,提高页面加载速度。
性能改进:
Tomcat 9 在性能方面进行了优化,特别是在 HTTP/2 协议的处理上,提升了吞吐量和降低了延迟。
默认使用 NIO 连接器,提供更好的性能和可伸缩性。
总结: Tomcat 9 主要关注于 Servlet 4.0 规范的实现和 HTTP/2 支持的增强,服务端推送等新特性进一步提升了 Web 应用的性能和用户体验。
Tomcat 10 是一个基于 Jakarta EE 平台的版本,实现了 Jakarta Servlet 5.0 和 Jakarta Server Pages 3.0 规范,最大的变化是命名空间从 javax.* 变更为 jakarta.*,主要特性包括:
Jakarta EE 平台迁移:
javax.* 变更为 jakarta.*,例如 javax.servlet.* 变为 jakarta.servlet.*,javax.servlet.http.* 变为 jakarta.servlet.http.*。这意味着原有的基于 Java EE 的 Web 应用需要进行代码迁移才能在 Tomcat 10 上运行。代码实践 - Jakarta Servlet 5.0 (Tomcat 10):
package com.example; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/jakarta-hello") public class JakartaHelloServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("Hello, Tomcat 10 (Jakarta EE)!"); } }
详解:
注意 import jakarta.servlet.annotation.WebServlet; 和 import jakarta.servlet.http.HttpServlet; 等包名,已经从 javax.* 变更为 jakarta.*。
这是 Jakarta EE 平台最显著的特征,也是 Tomcat 10 与之前版本最大的不兼容之处。
Jakarta Servlet 5.0 规范:
Jakarta Server Pages 3.0 规范:
代码迁移示例 (Java EE to Jakarta EE):
假设我们有一个基于 Tomcat 9 (Java EE) 的 Servlet 代码:
// Java EE (Tomcat 9 及之前版本) package com.example; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/jee-hello") public class JEEHelloServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("Hello, Java EE!"); } }
要将其迁移到 Tomcat 10 (Jakarta EE),我们需要修改包名:
// Jakarta EE (Tomcat 10 及之后版本) package com.example; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/jakarta-hello") public class JakartaHelloServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("Hello, Jakarta EE!"); } }
总结: Tomcat 10 是一个重要的过渡版本,它标志着 Tomcat 从 Java EE 时代迈向 Jakarta EE 时代。命名空间变更带来了不兼容性,但也为未来的 Jakarta EE 发展奠定了基础。对于新的项目,推荐使用 Tomcat 10 或更新的版本,但对于旧的 Java EE 项目,需要评估迁移成本。
Tomcat 11 基于 Tomcat 10,实现了 Jakarta Servlet 6.0 和 Jakarta Server Pages 4.0 规范,继续 Jakarta EE 的发展路线,主要特性包括:
Jakarta Servlet 6.0 规范:
HTTP Trailers 支持: Servlet 6.0 引入了对 HTTP Trailers 的支持,允许在 HTTP 响应的末尾发送额外的头部信息。
异步 Servlet API 改进: 对异步 Servlet API 进行了一些改进,例如增加了 AsyncContext.setTimeout() 方法的返回值,方便控制异步操作的超时。
代码实践 - Jakarta Servlet 6.0 HTTP Trailers:
package com.example; import java.io.IOException; import jakarta.servlet.AsyncContext; import jakarta.servlet.ServletException; import jakarta.servlet.WriteListener; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponseWrapper; @WebServlet(urlPatterns = "/trailers", asyncSupported = true) public class TrailerServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { AsyncContext asyncContext = request.startAsync(); HttpServletResponse wrappedResponse = new HttpServletResponseWrapper(response) { @Override public void setTrailerFields(java.util.function.Supplier<java.util.Map<String, String>> supplier) { super.setTrailerFields(supplier); } }; wrappedResponse.setTrailerFields(() -> { java.util.Map<String, String> trailers = new java.util.HashMap<>(); trailers.put("X-Trailer-Name", "Trailer Value"); return trailers; }); asyncContext.getResponse().setContentType("text/plain"); asyncContext.getResponse().setCharacterEncoding("UTF-8"); jakarta.servlet.ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream(); outputStream.setWriteListener(new WriteListener() { @Override public void onWritePossible() throws IOException { if (outputStream.isReady()) { outputStream.print("Hello, HTTP Trailers!"); asyncContext.complete(); } } @Override public void onError(Throwable t) { asyncContext.complete(); t.printStackTrace(); } }); } }
详解:
wrappedResponse.setTrailerFields(() -> { ... }); 设置 Trailer 头部信息,在响应的末尾发送。
需要使用 HttpServletResponseWrapper 包装原始的 HttpServletResponse 对象才能调用 setTrailerFields() 方法。
HTTP Trailers 可以用于发送校验和、数字签名等元数据,或者在流式响应中发送动态生成的头部信息。
Jakarta Server Pages 4.0 规范:
Java 版本要求:
总结: Tomcat 11 是 Jakarta EE 平台上的最新版本,实现了 Jakarta Servlet 6.0 和 Jakarta Server Pages 4.0 规范,继续增强了 HTTP/2 支持,并引入了 HTTP Trailers 等新特性。对于新的 Jakarta EE 项目,Tomcat 11 是一个理想的选择。
在选择 Tomcat 版本时,需要综合考虑以下因素:
Web 应用程序的规范要求: 确定应用程序需要使用的 Servlet 和 JSP 规范版本。如果应用程序使用了 Servlet 4.0 或 Jakarta Servlet 5.0/6.0 的新特性,则需要选择 Tomcat 9, 10 或 11。
Java 版本兼容性: 确保 Tomcat 版本与应用程序使用的 Java 版本兼容。Tomcat 7 要求 Java 6+,Tomcat 8 和 9 要求 Java 7+ 和 8+,Tomcat 10 和 11 推荐 Java 11+ 和 17+。
Jakarta EE 迁移: 如果应用程序是基于 Java EE 开发的,并且需要迁移到 Jakarta EE 平台,则需要选择 Tomcat 10 或 11,并进行代码迁移。
新特性需求: 如果需要使用 WebSocket, HTTP/2, HTTP Trailers 等新特性,则需要选择支持这些特性的 Tomcat 版本。
性能要求: Tomcat 的新版本通常在性能方面有所提升,如果对性能有较高要求,可以选择较新的版本。
社区支持和稳定性: Tomcat 7 和 8 已经停止维护,不建议在新项目中使用。Tomcat 9 仍然在维护中,但生命周期也接近尾声。Tomcat 10 和 11 是最新的版本,具有较长的维护周期和活跃的社区支持。
版本选择建议总结:
新项目 (Jakarta EE): Tomcat 11 是最佳选择,可以充分利用最新的 Jakarta EE 规范和 Tomcat 的新特性。如果考虑稳定性,Tomcat 10 也是一个不错的选择。
新项目 (Java EE): Tomcat 9 是最后的 Java EE 版本,如果不需要 Jakarta EE,并且希望使用最新的 Java EE 规范,可以选择 Tomcat 9。
现有 Java EE 项目:
Tomcat 7 或 8: 如果应用程序运行在 Tomcat 7 或 8 上,并且不需要新特性,可以继续使用,但需要注意安全更新和维护问题。建议考虑升级到 Tomcat 9 或 Jakarta EE 版本。
升级到 Tomcat 9: 升级到 Tomcat 9 可以获得更好的性能和对最新 Java EE 规范的支持,迁移成本相对较低。
迁移到 Tomcat 10/11 (Jakarta EE): 如果需要迁移到 Jakarta EE 平台,或者需要使用 Jakarta EE 的新特性,则需要评估代码迁移成本,并选择 Tomcat 10 或 11。
本章节详细对比了 Tomcat 7, 8, 9, 10 和 11 这五个主要版本的特性,从规范版本支持、Java 版本要求、新特性、性能改进等方面进行了深入分析,并提供了代码实践示例。通过对比,我们可以清晰地了解每个版本的特点和适用场景,为选择合适的 Tomcat 版本提供了参考。
Tomcat 的版本演进反映了 Web 技术的发展趋势,从 Java EE 到 Jakarta EE 的迁移是一个重要的里程碑。理解不同版本的特性差异,并根据实际需求做出明智的技术选型,是保证 Web 应用程序稳定、高效运行的关键。在选择 Tomcat 版本时,建议关注最新的稳定版本,并考虑未来的技术发展趋势,以便更好地应对 Web 应用开发和部署的挑战。