异常处理
Java Web 异常处理:Servlet 错误页面与异常拦截机制
在 Java Web 应用中,异常处理是保障应用稳定性和用户体验的关键环节。Servlet 容器提供了灵活的异常处理机制,允许开发者通过配置错误页面或自定义异常处理器,统一捕获和处理请求过程中出现的异常(如 404 资源不存在、500 服务器错误、自定义业务异常等)。本文将详细解析 Servlet 异常处理的配置方式、核心对象及实践技巧。
Servlet 异常处理的核心机制
Servlet 容器通过错误页面配置和异常属性传递实现异常处理:
- 当应用抛出未捕获的异常或返回特定错误码(如 404、500)时,容器会拦截请求;
- 根据预配置的规则(错误码或异常类型),将请求转发到指定的错误处理器(如 Servlet、JSP);
- 容器在请求域中设置异常相关属性,供错误处理器获取详细信息(如错误码、异常堆栈)。
异常处理的配置方式
XML 配置(web.xml)
通过 web.xml 的 <error-page> 标签配置错误页面,支持按错误码或异常类型匹配:
(1)按错误码配置
针对 HTTP 状态码(如 404、500)指定处理页面:
<!-- 处理 404 资源不存在错误 -->
<error-page>
<error-code>404</error-code>
<location>/error/404.jsp</location> <!-- 错误处理器路径 -->
</error-page>
<!-- 处理 500 服务器内部错误 -->
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
(2)按异常类型配置
针对特定异常类型(如 ArithmeticException、自定义 BusinessException)指定处理页面:
<!-- 处理算术异常(如除以零) -->
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/ExceptionHandler</location> <!-- 自定义 Servlet 处理器 -->
</error-page>
<!-- 处理自定义业务异常 -->
<error-page>
<exception-type>com.example.BusinessException</exception-type>
<location>/error/business.jsp</location>
</error-page>
注解配置(Servlet 3.0+)
Servlet 3.0 及以上支持通过注解 @WebServlet 的 errorPage 属性配置错误页面,或直接在异常处理器上使用 @WebServlet 并结合 web.xml 的 <error-page>。
示例:自定义异常处理 Servlet
public class ExceptionHandler extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理异常逻辑(见下文“获取异常信息”)
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
获取异常信息:请求域中的异常属性
当容器将请求转发到错误处理器时,会在 HttpServletRequest 的请求域中设置以下属性,用于获取异常详情:
| 属性键 | 类型 | 描述 |
|---|---|---|
javax.servlet.error.status_code |
Integer |
HTTP 状态码(如 404、500) |
javax.servlet.error.exception_type |
Class<?> |
异常类型的 Class 对象(如 ArithmeticException.class) |
javax.servlet.error.message |
String |
错误消息(如异常的 getMessage() 内容) |
javax.servlet.error.request_uri |
String |
触发异常的请求 URI(如 /user/123) |
javax.servlet.error.exception |
Throwable |
异常对象(可调用 printStackTrace() 打印堆栈) |
javax.servlet.error.servlet_name |
String |
触发异常的 Servlet 名称(如 UserServlet) |
示例:在异常处理器中获取信息
public class ExceptionHandler extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
// 从请求域获取异常信息
Integer statusCode = (Integer) req.getAttribute("javax.servlet.error.status_code");
String message = (String) req.getAttribute("javax.servlet.error.message");
String requestUri = (String) req.getAttribute("javax.servlet.error.request_uri");
Throwable exception = (Throwable) req.getAttribute("javax.servlet.error.exception");
// 输出错误信息
out.println("<h1>错误处理页面</h1>");
out.println("状态码:" + statusCode + "<br>");
out.println("错误消息:" + message + "<br>");
out.println("请求 URI:" + requestUri + "<br>");
// 打印异常堆栈
if (exception != null) {
out.println("<h3>异常堆栈:</h3>");
StringWriter sw = new StringWriter();
exception.printStackTrace(new PrintWriter(sw));
out.println("<pre>" + sw.toString() + "</pre>");
}
}
}
异常处理的执行流程
- 异常触发:
- 应用抛出未捕获的异常(如
throw new ArithmeticException()); - 容器返回特定 HTTP 状态码(如请求不存在资源时返回 404)。
- 应用抛出未捕获的异常(如
- 容器拦截:
容器根据web.xml的<error-page>配置,匹配错误码或异常类型。 - 转发请求:
容器将请求转发到<location>指定的错误处理器(Servlet 或 JSP),并在请求域中设置异常属性。 - 处理响应:
错误处理器读取请求域中的异常信息,生成友好的错误页面返回给客户端。
最佳实践
统一错误页面:
为常见错误码(404、500)和通用异常(Exception)配置统一处理页面,避免用户看到原始异常堆栈。区分环境:
- 开发环境:显示详细异常信息(便于调试);
- 生产环境:显示友好提示(如 “系统繁忙,请稍后再试”),同时记录详细日志。
日志记录:
在异常处理器中通过日志框架(如 Logback、Log4j)记录异常堆栈,便于问题排查:// 记录异常日志 Logger logger = LoggerFactory.getLogger(ExceptionHandler.class); logger.error("请求 {} 发生错误", requestUri, exception);
自定义异常类型:
定义业务异常(如BusinessException),并为其配置专属处理器,实现业务与系统异常的分离处理。避免循环错误:
确保错误处理器本身不会抛出异常,否则容器可能返回默认错误页面(如 Tomcat 的 404 页面)