java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台(即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se))的总称。
这篇文章主要介绍了 Java 过滤器 filter,通过过滤器,可以对来自客户端的请求进行拦截,进行预处理或者对最终响应给客户端的数据进行处理后再输出
Filter 过滤器技术。通过过滤器,可以对来自客户端的请求进行拦截,进行预处理或者对最终响应给客户端的数据进行处理后再输出。
要想使用 Filter 过滤器,非常简单,只要实现 Servlet API 中的 Filter 接口即可,同时在该 web 应用【WEB-INF】目录下的 web.xml 文件中配置
也就是说我们在 web 工程中光光写 Filter 过滤器的 Java 代码是不会起作用的,要在 web.xml 文件中对过滤器进行注册和映射,在学习 Filter 之前我们先来学习如何注册和映射
关于注册:
需要在 web.xml 文件中配置
需要在 web.xml 文件中配置
一个简单的对过滤器的注册和映射的示例:
- <filter>
- <filter-name>FilterDemo1</filter-name>
- <filter-class>com.bjpowernode.web.filter.FilterDemo1</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>FilterDemo1</filter-name>
- <url-pattern>/*</url-pattern>
- <dispatcher>REQUEST</dispatcher>
- <dispatcher>FORWARD</dispatcher>
- </filter-mapping>
在 Servlet API 中关于 Filter 举例了使用过滤器能用来做些什么:
这里我也说明下平时 Filter 能在哪些方面会被经常用到:
① Filter 可以作用在请求资源执行之前,进行权限检查,检查用户是否有权限,如有权限则放行请求;如果没有,则拒绝访问。
② Filter 可以作用在请求资源执行之前,对 Request 和 Response 对象进行预处理操作,从而实现一些 web 应用的全局性设置,比如解决中文乱码问题。
③ Filter 可以作用在最终响应输出之前,对输出 Response 对象中的数据进行处理,例如将输出的数据进行压缩。
Filter 只有 3 个方法:
其中 destroy() 方法和 init(…) 方法是生命周期方法,因为过滤器无论如何都要在请求任何资源之前进行,所以任何 Web 应用在部署的时候,服务器就会调用 Filter 过滤器的 init 方法进行初始化,而关于过滤器的销毁,则是将该过滤器移除或者服务器关闭就会执行 destory 方法。
而我们通常要使用过滤器处理请求,则重点在于 doFilter(…) 方法。当请求要经过一个过滤器的时候,就会由服务器调用 doFilter 方法。
我们先来看看一个带有过滤器 Filter 的 web 应用的请求和响应流程:
记住:从请求到响应这个流程会经过 Filter 对象两次!
在 doFilter 这一个方法中就可以对着两次经过的过程进行处理,那么这里就有一个问题了,如果能通过过滤器,那么就到过滤器后面了,貌似应该是执行完 doFilter 方法了,而服务器的响应又经过过滤器,难道又要执行 doFilter 方法一次?但是这个方法里面的代码不是也有处理最开始请求的吗?
这就跟 doFilter 方法中的第三个参数 FilterChain 有关了,FilterChain 对象是过滤器链,这个我们稍后会介绍。在 FilterChain 对象中只有一个方法:
= 也是叫 doFilter 方法 (千万别和 Filter 接口的 doFilter 方法弄混了)。简单的说下这个方法,只要调用了这个方法,就会将请求交给后面一个 Filter 进行过滤 (一个 Web 应用中可以有多个 Filter),如果该 Filter 是最后一个,那么调用该方法则将执行请求,也就是到我们的应用中获取资源。
因此从请求到响应这个流程经过 Filter 的两次处理分别是在 FilterChain.doFilter 方法的前面和后面!如下图所示:
那么下面我们就先以一个简单的例子来熟悉下 Filter 吧:
例 1:创建 web 工程 FilterLearning,创建一个 FilterDemo1 类,同时这个类要实现 javax.servlet.Filter 接口。如下代码:
- public class FilterDemo implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- System.out.println("hello filter");
- }
- //此处省略init方法和destory方法
- }
写好 Filter 的 Java 代码还没完,还要在 web 应用下的 web.xml 文件中配置如下信息:
- <filter>
- <filter-name>FilterDemo1</filter-name>
- <filter-class>com.bjpowernode.web.filter.FilterDemo</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>FilterDemo1</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
注意:因为我在
我们在 index.jsp 中简单的使用 JSP 脚本来演示如果有请求来就输入一段文本到控制台上:
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <!DOCTYPE html>
- <html>
- <head>
- <title>index</title>
- </head>
- <body>
- <%
- System.out.println("Long live SD !");
- %>
- </body>
- </html>
接下来将该 web 应用部署到服务器中,我们就访问 index.jsp,以下是我们访问了 index.jsp 后控制台的情况:
首先,我们可以保证在我们访问 index.jsp 后这个请求确实经过了 Filter 过滤器,但是我们的请求好像就只到过滤器而没有到我们真正需要的资源 index.jsp?这是因为我们没有在 Filter 的 doFilter 方法中调用过滤器链 FilterChain 对象的 doFilter 方法,自然无法将请求继续往后面传递。我们将在例 2 中修改。
例 2:我们将例 1 中的 FilterDemo1 类进行修改,使其能访问到我们所需要的资源,很简单,在 doFilter 的方法中添加过滤器链 FilterChain 对象的 doFilter 方法即可:
- public class FilterDemo implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- System.out.println("hello filter");
- chain.doFilter(request, response);
- }
- //此处省略init方法和destory方法
- }
其他如 web.xml 中的配置和 index.jsp 中的代码保持不变,现在我们再来访问下该 web 应用中的 index.jsp,并观察控制台:
可以看到我们的请求经过过滤器,执行了过滤器的一段代码 (System.out.println("hello filter")),然后将请求继续执行!正是因为 FilterChain.doFilter 方法才使我们通过过滤器继续向后寻找我们所需的资源。
那么还记得我们之前说过的从请求到响应会经过两次过滤器吗,是的在获取了我们所需的资源后还会到过滤器一趟,而至于这时候是否将响应再做处理取决于过滤器链 FilterChain.doFilter 方法后面还是否有代码。我们将在例 3 中完整的展现从请求到响应经过过滤器两次的流程。
例 3:我们将例 2 中的 FilterDemo1 类进行修改,只要在 FilterChain.doFilter 方法后面添加代码,就是第二次(即响应)经过过滤器所要执行的处理:
- public class FilterDemo implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- System.out.println("hello filter");
- chain.doFilter(request, response);
- System.out.println("goodbye filter");
- }
- //此处省略init方法和destory方法
- }
其他如 web.xml 中的配置和 index.jsp 中的代码保持不变,现在我们再来访问下该 web 应用中的 index.jsp,并观察控制台:
这个结果证明了从请求到响应确实经过过滤器两次,同时也说明了在 Filter 的 doFilter 方法中 "过滤——取资源——再过滤" 执行的顺序。
现在我们再回到 Filter 接口的 init 方法,我们可以看到在这个方法内有一个参数 FilterConfig,这个是由服务器传给我们的对象。如果我们在 web.xml 文件中配置了过滤器的初始化参数,就可以通过该 FilterConfig 对象来在代码中获取使用。
这个过滤器参数的初始化配置可以在
FilterConfig 有如下方法:
当然如果我们是要获取配置的初始化参数则只需关注 getInitParameter 方法或 getInitParameterNames 方法。
一般来说我们可以在 init 方法中获取配置初始化参数并进行处理;也可以通过对象引用将 FilterConfig 对象在 doFilter 方法中处理参数,如例 4 所示。
例 4:在 web.xml 文件中配置过滤器和初始化参数:
- <filter>
- <filter-name>FilterDemo1</filter-name>
- <filter-class>com.bjpowernode.web.filter.FilterDemo1</filter-class>
- <init-param>
- <param-name>Love</param-name>
- <param-value>LRR</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>FilterDemo1</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
在 Java 中编写 Filter 接口的实现类 FilterDemo1:
- public class FilterDemo implements Filter {
- private FilterConfig filterConfig ;
- @Override
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- String value = filterConfig.getInitParameter("Love");
- System.out.println(value);
- }
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- this.filterConfig = filterConfig;
- }
- //此处省略destroy方法
- }
因为配置的原因,所以我们随便访问个资源都可以经过该过滤器,那么就访问 index.jsp 好了,看看控制台的结果:
正如我们在 web.xml 文件所配置的初始化参数一样。
上面介绍的都是只有一个 Filter 过滤器的情况下,有时候我们会因为要过滤的功能不同添加多个过滤器,这就有一个顺序的问题了,尤其是从取得资源后再返回到过滤器的顺序。下面这张图就能很清晰的看到我们要注意的顺序了:
例 5:来写两个 Filter 来说明下从请求到响应过滤器的处理顺序。
创建一个 web 工程,创建一个 FilterDemo1 类,同时这个类要实现 javax.servlet.Filter 接口。如下代码:
- public class FilterDemo1 implements Filter {
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
- ServletException {
- System.out.println("Hello filter 1");
- chain.doFilter(request, response);
- System.out.println("Goodbye filter 1");
- }
- //此处省略init方法和destroy方法
- }
创建第二个 Filter 接口实现类 FilterDemo2,代码如下:
- public class FilterDemo2 implements Filter {
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
- ServletException {
- System.out.println("Hello filter 2");
- chain.doFilter(request, response);
- System.out.println("Goodbye filter 2");
- }
- //此处省略init方法和destroy方法
- }
过滤器要想能被服务器调用,还必须要在该 web 工程下的 web.xml 中配置过滤器及其映射,而这个配置的顺序就是影响多个过滤器工作先后的顺序:
- <filter>
- <filter-name>FilterDemo1</filter-name>
- <filter-class>com.bjpowernode.web.filter.FilterDemo1</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>FilterDemo1</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <filter>
- <filter-name>FilterDemo2</filter-name>
- <filter-class>com.bjpowernode.web.filter.FilterDemo2</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>FilterDemo2</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
而我们要访问的资源文件就以 index.jsp 为例好了,那么我们用一段 JSP 脚本通过在控制台打印来验证过滤器工作的顺序过程:
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <!DOCTYPE HTML>
- <html>
- <head>
- <title>index</title>
- </head>
- <body>
- <%
- System.out.println("Long live SD !");
- %>
- </body>
- </html>
现在启动服务器,部署该工程,通过访问 index.jsp 来看看控制台情况:
来源: http://www.phperz.com/article/18/0104/356008.html