Filter介绍
Filter可认为是Servlet的一种“变种”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。它与Servlet的区别在于:它不能直接向用户生成响应。完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter有如下几个用处。
- 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。
- 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。
- 在HttpServletResponse到达客户端之前,拦截HttpServletResponse。
- 根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
Filter有如下几个种类。
- 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。
- 日志Filter:详细记录某些特殊的用户请求。
- 负责解码的Filter:包括对非标准编码的请求解码。
- 能改变XML内容的XSLT Filter等。
- Filter可负责拦截多个请求或响应;一个请求或响应也可被多个请求拦截。
创建一个Filter只需两个步骤:
- 建Filter处理类;
- web.xml文件中配置Filter。
下面先介绍一个简单的记录日志的Filter,这个Filter负责拦截所有的用户请求,并将请求的信息记录在日志中。
public class LogFilter implements Filter { /** * FilterConfig可用于访问Filter的配置信息 */ private FilterConfig config; @Override public void init(FilterConfig filterConfig) throws ServletException { this.config = config; } /** * 执行过滤的核心方法 * * @param request * @param response * @param chain * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //---------下面代码用于对用户请求执行预处理--------- //获取ServletContext对象,用于记录日志 ServletContext context = this.config.getServletContext(); long before = System.currentTimeMillis(); System.out.println("开始过滤..."); //将请求转换成HttpServletRequest请求 HttpServletRequest hrequest = (HttpServletRequest) request; //记录日志 context.log("Filter已经截获到用户的请求地址: " + hrequest.getServletPath()); //Filter只是链式处理,请求依然放行到目的地址 chain.doFilter(request, response); //---------下面代码用于对服务器响应执行后处理--------- long after = System.currentTimeMillis(); //记录日志 context.log("过滤结束"); //再次记录日志 context.log("请求被定位到" + hrequest.getRequestURI() + "所花的时间为: " + (after - before)); } @Override public void destroy() { this.config = null; } }
上面程序实现了doFilter()方法,实现该方法就可实现对用户请求进行预处理,也可实现对服务器响应进行后处理——它们的分界线为是否调用了chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。
在上面的请求Filter中,仅在日志中记录请求的URL,对所有的请求都执行chain.doFilter (request,reponse)方法,当Filter对请求过滤后,依然将请求发送到目的地址。如果需要检查权限,可以在Filter中根据用户请求的HttpSession,判断用户权限是否足够。如果权限不够,直接调用重定向即可,无须调用chain.doFilter(request,reponse)方法。
FirstFilter.java
public class FirstFilter implements Filter { @Override public void destroy() { System.out.println("1st destroy()..."); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("before invoke 1stFilter‘s chain.doFilter() .."); chain.doFilter(request, response); System.out.println("after invoke 1stFilter‘s chain.doFilter() .."); } @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("1stFilter init()..."); } }
SecondFilter.java
public class SecondFilter implements Filter { @Override public void destroy() { System.out.println("2ndFilter destroy()..."); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("before invoke 2ndFilter‘s chain.doFilter() .."); chain.doFilter(request, response); System.out.println("after invoke 2ndFilter‘s chain.doFilter() .."); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("2ndFilter init()..."); } }
MyServlet.java
public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servlet doGet be invoked..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub this.doGet(req, resp); } }
web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <filter> <filter-name>firstFilter</filter-name> <filter-class>com.winner.FirstFilter</filter-class> </filter> <filter> <filter-name>secondFilter</filter-name> <filter-class>com.winner.SecondFilter</filter-class> </filter> <filter-mapping> <filter-name>secondFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>firstFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.winner.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/MyServlet</url-pattern> </servlet-mapping> </web-app>
然后发布,发现打印的日志如下:
1stFilter init()...
2ndFilter init()...
这里过滤器初始化好了。
当我们访问我们的 应用:http://localhost:8080/MyServlet
before invoke 2ndFilter‘s chain.doFilter() ..
before invoke 1stFilter‘s chain.doFilter() ..
servlet doGet be invoked...
after invoke 1stFilter‘s chain.doFilter() ..
after invoke 2ndFilter‘s chain.doFilter() ..
当我们将web.xml中filter的位置进行调整后(注意filter-mapping的顺序):
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <filter> <filter-name>firstFilter</filter-name> <filter-class>com.winner.FirstFilter</filter-class> </filter> <filter> <filter-name>secondFilter</filter-name> <filter-class>com.winner.SecondFilter</filter-class> </filter> <filter-mapping> <filter-name>firstFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>secondFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.winner.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/MyServlet</url-pattern> </servlet-mapping> </web-app>
当我们访问我们的 应用:http://localhost:8080/MyServlet
before invoke 1stFilter‘s chain.doFilter() ..
before invoke 2ndFilter‘s chain.doFilter() ..
servlet doGet be invoked...
after invoke 2ndFilter‘s chain.doFilter() ..
after invoke 1stFilter‘s chain.doFilter() ..