1. 简介
本文将带你掌握在 Jakarta EE Servlet 应用中如何优雅地处理异常,确保系统出错时能返回预期、友好的响应,而不是把堆栈直接暴露给用户——这在生产环境中是大忌,踩坑无数。
我们不会引入 Spring 或其他框架,纯粹基于原生 Servlet API 实现,适合想深入理解底层机制的开发者。
2. Jakarta EE Servlet 异常示例
先定义一个简单的 Servlet,使用 @WebServlet
注解注册接口路径。它的 doGet
方法会主动抛出一个异常,用于模拟运行时错误:
@WebServlet(urlPatterns = "/randomError")
public class RandomErrorServlet extends HttpServlet {
@Override
protected void doGet(
HttpServletRequest req,
HttpServletResponse resp) {
throw new IllegalStateException("Random error");
}
}
这个接口 /randomError
一旦被访问,就会触发 IllegalStateException
。接下来我们看看默认行为是什么。
3. 默认异常处理机制
将应用部署到 Servlet 容器(比如 Tomcat),假设应用上下文路径为:http://localhost:8080/jakarta-servlets
。
访问 http://localhost:8080/jakarta-servlets/randomError
,你会看到类似下面的默认错误页面:
✅ 这个页面由 Servlet 容器(如 Tomcat)自动生成。
⚠️ 缺点很明显:信息过于详细,可能泄露内部实现,存在安全风险。
📌 默认行为可通过容器级别配置(如 Tomcat 的 ErrorReportValve
)或应用级别 web.xml
覆盖。
4. 自定义异常处理
为了提升用户体验和安全性,我们需要自定义错误处理逻辑。可以通过 web.xml
配置两种类型的错误映射:
- 状态码映射:针对特定 HTTP 状态码(如 404、500)跳转到指定页面或 Servlet
- 异常类型映射:捕获特定异常类,并交由指定组件处理
4.1 使用 HTML 页面处理状态码异常
比如我们想为 404 错误提供一个友好的提示页,可以在 web.xml
中配置:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<error-page>
<error-code>404</error-code>
<location>/error-404.html</location> <!-- 文件路径: /src/main/webapp/error-404.html -->
</error-page>
</web-app>
只要请求的资源不存在(返回 404),容器就会自动跳转到 /error-404.html
。你可以在该页面中设计品牌化的错误提示,提升产品质感。
4.2 使用 Servlet 处理异常类型
更灵活的方式是使用一个专门的 ErrorHandlerServlet
来处理异常。比如我们希望捕获所有 Exception
类型的异常:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/errorHandler</location>
</error-page>
</web-app>
然后实现对应的 ErrorHandlerServlet
,通过请求中的标准错误属性获取上下文信息:
@WebServlet(urlPatterns = "/errorHandler")
public class ErrorHandlerServlet extends HttpServlet {
@Override
protected void doGet(
HttpServletRequest req,
HttpServletResponse resp) throws IOException {
resp.setContentType("text/html; charset=utf-8");
try (PrintWriter writer = resp.getWriter()) {
writer.write("<html><head><title>Error description</title></head><body>");
writer.write("<h2>Error description</h2>");
writer.write("<ul>");
Arrays.asList(
ERROR_STATUS_CODE,
ERROR_EXCEPTION_TYPE,
ERROR_MESSAGE)
.forEach(e ->
writer.write("<li>" + e + ":" + req.getAttribute(e) + " </li>")
);
writer.write("</ul>");
writer.write("</html></body>");
}
}
}
现在访问 http://localhost:8080/jakarta-servlets/randomError
,你会看到由 ErrorHandlerServlet
渲染的错误详情页。
📌 关键点:
- ✅
ERROR_STATUS_CODE
、ERROR_EXCEPTION_TYPE
、ERROR_MESSAGE
是 Servlet 规范定义的标准请求属性 - ✅ 可通过
req.getAttribute(ERROR_EXCEPTION)
获取原始异常对象 - ❌ 示例中捕获
java.lang.Exception
太宽泛,实际项目中应细化到具体异常类型,避免误伤
日志记录建议
在 ErrorHandlerServlet
中,推荐使用容器提供的日志机制记录异常,便于排查:
Exception exception = (Exception) req.getAttribute(ERROR_EXCEPTION);
if (IllegalArgumentException.class.isInstance(exception)) {
getServletContext()
.log("Error on an application argument", exception);
}
⚠️ 注意:ServletContext.log()
是基础日志功能,生产环境建议集成 SLF4J + Logback 或 Log4j2 实现更强大的日志控制。
5. 总结
本文演示了 Jakarta EE 原生 Servlet 中的异常处理机制:
- ✅ 默认错误页由容器生成,不适合生产使用
- ✅ 可通过
web.xml
配置状态码或异常类型的自定义处理逻辑 - ✅ 使用
ErrorHandlerServlet
能实现高度定制化的错误响应 - ✅ 结合
ServletContext.log()
实现基本日志追踪
整个过程无需引入第三方框架,简单粗暴但足够有效。适合轻量级项目或作为深入理解 Java Web 机制的起点。
示例代码已托管至 Servlets 教程仓库
邮箱支持:support@baeldung.com