JavaWeb三大组件之Filter
对请求或者响应进行拦截,并做额外的逻辑处理
filter能在一个请求访问目标资源之前对其进行拦截然后做某些逻辑处理,例如权限检查,也可以在一个输出响应到达客户端之前对其进行拦截并做一些额外的操作(例如,设置响应编码格式)。
二、接口定义
public abstract interface Filter{ public abstract void init(FilterConfig filterConfig) throws ServletException; public abstract void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,ServletException; public abstract void destroy(); }
三、执行原理分析
当客户端发送一个请求访问某个资源时,Servlet容器会根据web.xml中的配置找出该请求所有匹配的过滤器,并保存到一个数组中,从而形成一个过滤器链,链中的每个过滤器都是一个处理节点。然后通过调用Tomcat服务器中的ApplicationFilterChain对象中的doFilter方法,不断迭代执行数组中的过滤器中的doFilter方法对请求和响应进行额外的逻辑处理(因为过滤器中的doFilter方法会重新调用FilterChain中的doFilter方法)。
Filter其实就是基于回调函数实现的责任链模式的应用。
模拟Servlet容器执行过滤器的场景:
import java.util.List; class Scratch { public static void main(String[] args) { new ApplicationFilterChain().doFilter(new ServletRequest(), new ServletResponse()); } public static interface Filter { void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain); } /** * 自定义过滤器 */ public static class MyFilter1 implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) { System.out.println("即将执行过滤器1"); //回调方法,用于调用链中的下一个过滤器 filterChain.doFilter(request, response); System.out.println("已经执行过滤器1"); } } /** * 自定义过滤器 */ public static class MyFilter2 implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) { System.out.println("即将执行过滤器2"); //回调方法,用于调用链中的下一个过滤器 filterChain.doFilter(request, response); System.out.println("已经执行过滤器2"); } } /** * 自定义过滤器 */ public static class MyFilter3 implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) { System.out.println("即将执行过滤器3"); //回调方法,用于调用链中的下一个过滤器 filterChain.doFilter(request, response); System.out.println("已经执行过滤器3"); } } /** * 模拟过滤器链接口 */ public static interface FilterChain { void doFilter(ServletRequest request, ServletResponse response); } /** * 模拟ApplicationFilterChain */ public static class ApplicationFilterChain implements FilterChain { //模拟注册的过滤器,保存在一个数组中 Filter[] filters = new Filter[]{new MyFilter1(), new MyFilter2(), new MyFilter3()}; //用于保存遍历过滤器数组的下标 private int pos = 0; /** * 开始执行过滤器逻辑 */ public void doFilter(ServletRequest request, ServletResponse response) { //pos小于过滤器数组中的元素说明过滤器还没执行完 if (pos < filters.length) { //调用过滤器的doFilter方法,并将this这个当前对象传递进去 filters[pos++].doFilter(request, response, this); } } } /** * 模拟请求对象 */ public static class ServletRequest { } /* * 模拟响应对象 * */ public static class ServletResponse { } }
结果如下:
四、Filter的生命周期
Filter类似于Servlet,其对象的创建和销毁由Servlet容器负责。当Servlet容器启动的时候,将创建Filter实例对象,并调用其init()方法进行一些初始化操作。当Servlet容器关闭或者重启时,将调用Filter对象的destory()方法,以释放相应的资源。init()和destory()方法在Filter对象的整个生命周期中都只仅会执行一次。当客户端有多个并发请求访问某个资源的时候,doFilter方法可能同时在多个线程环境中被执行,因此有可能出现并发安全问题,需要尽量确保doFilter方法中不会出现操作全局变量的情况。
五、配置Filter
1、xml方法配置
<filter> <filter-name>MyFilter</filter-name> <filter-class>com.demo.web.MyFilter</filter-class> </filter> <servlet-mapping> <servlet-name>MyFilter</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
使用xml配置注解时,默认配置在前面的Filter会先执行。
另外,filterMapping中还可以通过<dispatcher>节点配置指示Servlet容器拦截何种方式的资源。
1、REQUEST:只拦截用户直接访问的资源(默认)。
2、INCLUDE:只拦截通过RequestDispatcher的include()方法访问的资源。
3、FORWARD:只拦截通过RequestDispatcher的forward()方法访问的资源。
3、ERROR:只拦截通过声明式异常处理机制调用的资源。
2、使用注解配置
@WebFilter(filterName="MyFilter",urlPatterns={"/*"})
六、示例代码
MyFilter.java
package com.fengjr.demo01; import javax.servlet.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding("utf-8"); servletResponse.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/html;charset=utf-8"); servletResponse.getWriter().write("<br/>拦截请求,在执行业务逻辑方法之前<br/>"); servletRequest.setAttribute("str", "Filter"); filterChain.doFilter(servletRequest, servletResponse); servletResponse.getWriter().write("<br/>拦截响应,执行业务逻辑之后追加此段文本。"); } public void destroy() { } }
web.xml配置
<filter> <filter-name>MyFilter</filter-name> <filter-class>com.fengjr.demo01.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/MyServlet</url-pattern> </filter-mapping>