在Java Web开发中,Servlet Filter是一个非常强大的工具,它可以拦截请求和响应,执行诸如日志记录、权限验证、数据预处理等操作。然而,很多时候我们希望Filter只在特定的场景下生效,比如仅在处理错误页面时触发。这就需要我们深入了解DispatcherType
的概念及其应用。本文将通过一个具体的实例,展示如何利用DispatcherType
来控制Filter的行为。
DispatcherType简介
在Servlet规范中,DispatcherType
用于定义请求的分发类型,它决定了哪些Filter会被应用到请求上。ServletRequest.getDispatcherType()
方法可以获取当前请求的分发类型。以下是几种常见的DispatcherType
:
REQUEST
:初始请求的分发类型。FORWARD
:通过RequestDispatcher.forward()
转发的请求。INCLUDE
:通过RequestDispatcher.include()
包含的请求。ASYNC
:异步请求。ERROR
:由容器的错误处理机制转发到错误页面的请求。
实例:创建一个仅在错误时触发的Filter
1. 创建Servlet
首先,我们创建一个可能会抛出异常的Servlet。这个Servlet将作为我们的测试入口点。
@WebServlet(name = "currencyRateServlet", urlPatterns = {"/app/*"})
public class AppServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
DispatcherType dispatcherType = req.getDispatcherType();
String requestURI = req.getRequestURI();
writer.write(String.format("response at %s, DispatcherType: %s%n", requestURI, dispatcherType));
if (requestURI.endsWith("test")) {
writer.println("AppServlet response at: " + requestURI);
} else {
int i = 10 / 0; // 故意抛出异常
}
}
}
2. 配置错误页面
接下来,我们需要在web.xml
中配置一个错误页面,以便在发生异常时能够捕获并处理。
<?xml version="1.0" encoding="UTF-8"?>
<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>
<location>/error-handlers/default-error-handler</location>
</error-page>
</web-app>
3. 创建错误处理Servlet
这个Servlet将作为错误页面的处理器,用于捕获和处理异常。
@WebServlet(name = "errorHandlerServlet", urlPatterns = {"/error-handlers/default-error-handler"})
public class ErrorHandlerServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println("Error Handler Servlet:");
writer.printf("dispatcherType: %s%n", req.getDispatcherType());
}
}
4. 创建仅在错误时触发的Filter
现在,我们创建一个Filter,它只在DispatcherType.ERROR
时被触发。
@WebFilter(filterName = "errorFilter", urlPatterns = "/*", dispatcherTypes = {DispatcherType.ERROR})
public class ErrorFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(req, resp);
PrintWriter writer = resp.getWriter();
writer.println("In the filter:");
Exception exception = (Exception) req.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
writer.printf("exception: %s%n", exception);
Integer code = (Integer) req.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
writer.printf("status_code: %s%n", code);
String requestUri = (String) req.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);
writer.printf("request_uri: %s%n", requestUri);
writer.printf("dispatcherType: %s%n", req.getDispatcherType());
}
@Override
public void destroy() {
}
}
测试与输出
运行项目后,访问一个未映射到AppServlet
的URI,或者访问一个会抛出异常的URI,我们可以看到以下输出:
- 正常请求:
ErrorFilter
不会被触发。 - 错误请求:
ErrorFilter
会被触发,并打印出错误信息,包括异常类型、状态码、请求URI和DispatcherType
。
扩展:通过web.xml
配置DispatcherType
如果使用XML配置而非注解,可以在web.xml
中这样配置:
<filter-mapping>
<filter-name>errorFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
动态注册Filter
我们还可以通过程序动态注册Filter,并指定DispatcherType
:
@WebListener
public class MyContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
FilterRegistration.Dynamic fr = sce.getServletContext().addFilter("errorFilter", ErrorFilter.class);
EnumSet<DispatcherType> dts = EnumSet.of(DispatcherType.ERROR);
fr.addMappingForUrlPatterns(dts, false, "/*");
}
}
总结
通过DispatcherType
,我们可以精确地控制Filter的触发场景,从而实现更加灵活的请求处理逻辑。无论是通过注解、XML配置还是动态注册,DispatcherType
都为我们提供了强大的工具,帮助我们更好地管理Servlet生命周期中的各种请求类型。