一、什么是Web过滤器
Servlet API 很久以前就已成为企业应用开发的基石,而 Servlet 过滤器则是对 J2EE 家族的相对较新的补充。
Servlet 过滤器是可插入的 Web 组件,它允许我们实现 Web 应用程序中的预处理和后期处理逻辑。过滤器支持 servlet 和 JSP 页面的基本请求处理功能,比如日志记录、性能、安全、会话处理、XSLT 转换,等等。 过滤器最初是随 Java Servlet 2.3 规范发布的。
Servlet 过滤器是什么?
Servlet 过滤器是小型的 Web
组件,它们拦截请求和响应,以便查看、提取或以某种方式操作正在客户机和服务器之间交换的数据。过滤器是通常封装了一些功能的 Web
组件,这些功能虽然很重要,但是对于处理客户机请求或发送响应来说不是决定性的。典型的例子包括记录关于请求和响应的数据、处理安全协议、管理会话属性,
等等。过滤器提供一种面向对象的模块化机制,用以将公共任务封装到可插入的组件中,这些组件通过一个配置文件来声明,并动态地处理。
Servlet 过滤器中结合了许多元素,从而使得过滤器成为独特、强大和模块化的 Web 组件。也就是说,Servlet 过滤器是:
声明式的:过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明。这样允许添加和删除过滤器,而无需改动任何应用程序代码或 JSP 页面。
动态的:过滤器在运行时由 Servlet 容器调用来拦截和处理请求和响应。
灵活的:过滤器在 Web
处理环境中的应用很广泛,涵盖诸如日志记录和安全等许多最公共的辅助任务。过滤器还是灵活的,因为它们可用于对来自客户机的直接调用执行预处理和后期处
理,以及处理在防火墙之后的 Web 组件之间调度的请求。最后,可以将过滤器链接起来以提供必需的功能。
模块化的:通过把应用程序处理逻辑封装到单个类文件中,过滤器从而定义了可容易地从请求/响应链中添加或删除的模块化单元。
可移植的:与 Java 平台的其他许多方面一样,Servlet 过滤器是跨平台和跨容器可移植的,从而进一步支持了 Servler 过滤器的模块化和可重用本质。
可重用的:归功于过滤器实现类的模块化设计,以及声明式的过滤器配置方式,过滤器可以容易地跨越不同的项目和应用程序使用。
透明的:在请求/响应链中包括过滤器,这种设计是为了补充(而不是以任何方式替代)servlet 或 JSP 页面提供的核心处理。因而,过滤器可以根据需要添加或删除,而不会破坏 servlet 或 JSP 页面。
所以 Servlet
过滤器是通过一个配置文件来灵活声明的模块化可重用组件。过滤器动态地处理传入的请求和传出的响应,并且无需修改应用程序代码就可以透明地添加或删除它们。最后,过滤器独立于任何平台或者 Servlet 容器,从而允许将它们容易地部署到任何相容的 J2EE 环境中。
二、第一个过滤器
package com.po; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class FirstFilter implements Filter { @Override public void destroy() { System.out.println("destroy"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter: " + request.toString()); chain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init: " + filterConfig.toString()); } }
过滤器需要实现 Filter 接口:
init():这个方法在容器实例化过滤器时被调用,它主要设计用于使过滤器为处理做准备。该方法接受一个 FilterConfig 类型的对象作为输入。
doFilter():
与 servlet 拥有一个 service() 方法(这个方法又调用 doPost() 或者
doGet())来处理请求一样,过滤器拥有单个用于处理请求和响应的方法——doFilter()。这个方法接受三个输入参数:一个
ServletRequest、response 和一个 FilterChain 对象。
destroy():正如您想像的那样,这个方法执行任何清理操作,这些操作可能需要在自动垃圾收集之前进行。展示了一个非常简单的过滤器,它跟踪满足一个客户机的 Web 请求所花的大致时间。
三、配置 web.xml ,启用过滤器
先来看下这个图:
现在,我们在 web.xml 中,添加这些 xml 内容:
<!-- 过滤器配置 --> <filter> <filter-name>FirstFilter</filter-name> <filter-class>com.po.FirstFilter</filter-class> </filter> <filter-mapping> <filter-name>FirstFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
filter 标签用来声明一个过滤器,每个过滤器只能有一个filter标签。
filter-mapping 标签用来指定过滤器的作用对象。上面的示例中 url-pattern 设定为了 "/*", 代表对所有jsp请求有效。
更改为 web.xml 后,需要重启 tomcat 服务器。
在重启服务器时,我们可以在控制台日志中看到我们的过滤器 init() 被调用了。
在浏览器 中访问一个页面,doFilter 会被执行。
四、 常见问题
1. 过滤器是否能够改变用户请求的WEB资源呢? 也就是能否改变用户请求的路径?
回答: 能。比如检测到用户没有登录,可以请WEB资源指向到登录页面。
2. 过滤器能否直接返回数据,能不能直接处理用户请求?
回答: 不能。因为过滤器不是一样标准的 Servlet ,它不能直接返回数据。它要是么指向一个WEB资源,要么是重定向到其它的WEB资源。
五、多个过滤器
WEB服务程序是可以支持多个过滤器的。
假如一个URL和多个过滤器指定的URL相符时,会生成一个过滤器链。
服务器会按照web.xml中过滤器定义的先后顺序组装成过滤器链。
过滤器链的执行过程:
六、过滤器的分类
过滤器默认是 Request 类型。
FORWARD 过滤器:
<filter> <filter-name>FirstFilter</filter-name> <filter-class>com.po.FirstFilter</filter-class> </filter> <filter-mapping> <filter-name>FirstFilter</filter-name> <url-pattern>/index.jsp</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping>
INCLUDE 过滤器和 FORWARD 声明类似,将dispatcher改为INCLUDE就可以了。
错误过滤器:
<!-- 错误过滤器 --> <error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <filter> <filter-name>ErrorFilter</filter-name> <filter-class>com.po.ErrorFilter</filter-class> </filter> <filter-mapping> <filter-name>ErrorFilter</filter-name> <url-pattern>/error.jsp</url-pattern> <dispatcher>ERROR</dispatcher> </filter-mapping>
在 Servlet 3.0 及之后的版本中,增加了异步支持。还增加使用注解的方式来定义过滤器(也就是说不需要我们修改 web.xml)了。
七、 @WebFilter 注解定义过滤器
我们先来看看 @WebFilter的常用属性。
示例: (错误过滤器)
package com.po; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; @WebFilter( filterName="ErrorFilter", value={"/error.jsp"}, dispatcherTypes={javax.servlet.DispatcherType.ERROR}) public class ErrorFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter start."); chain.doFilter(request, response); System.out.println("doFilter end."); } }
上面的代码,使用注解来定义过滤器。
下面是异步过滤的示例:
首先是新建一个Servlet,这个Servlet用来处理复杂的异步事务。
package servlet; import java.io.IOException; import javax.servlet.AsyncContext; 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(name="AsyncServlet",asyncSupported=true,urlPatterns={"/servlet/AsyncServlet"},description="异步过滤器示例Servlet") public class AsyncServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("业务请求开始."); AsyncContext context = req.startAsync(); // 开启异步线程 new Thread(new Executor(context)).start();
// req.getRequestDispatcher("/index.jsp").forward(req, resp);
// resp.getWriter().close(); System.out.println("业务请求结束."); } public class Executor implements Runnable { private AsyncContext context; public Executor(AsyncContext context) { this.context = context; } @Override public void run() { try { // 复杂业务处理 System.out.println(context.getRequest().getScheme()); Thread.sleep(10 * 1000); System.out.println("业务执行完成."); } catch (InterruptedException e) { e.printStackTrace(); } } } }
接下来,建立一个异步过滤器。
package servlet; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; @WebFilter(filterName="AsyncFilter", urlPatterns={"/servlet/AsyncServlet"}, asyncSupported=true, dispatcherTypes={javax.servlet.DispatcherType.ASYNC} ) public class AsyncFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); } }
异步过滤器的特性就是能很快的响应用户请求,复杂的业务处理过程放到新的线程中执行。
当然了,在我们上面的示例代码中, AsyncServlet.onGet里面只是启动一个业务处理线程,并没写响应的代码,所以会导致客户端出现一个 500 错误。实际业务中,输出
【感谢】