Filter 可认为是 Servlet 的一种“加强版”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的 处理链;
Filter 也可对用户请求生成响应,这一点与 Servlet 相同,但实际上很少会使用 Filter 向用户请求生成响应。
使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再对服务器响应进行后处理。
Filter 的几个用处:
1、在HttpServletRequest 到达 Servlet 之前,拦截客户的HttpServletRequest;
2、根据需要检查 HttpServletRequest, 也可以修改 HttpServletRequest 头和数据;
3、在 HttpServletResponse 到达客户端之前,拦截 HttpServletResponse;
4、根据需要检查 HttpServletResponse,也可以修改HttpServletResponse 头和数据;
Filter 的几个种类:
1、用户授权的 Filter:Filter 负责检查用户请求,根据请求过滤用户非法请求;
2、日志 Filter:详细记录某些特殊的用户请求;
3、负责解码的 Filter:包括对非标准编码的请求解码;
4、能改变 XML 内容的 XSLT Filter等。
5、Filter 可负责拦截多个请求或响应;一个请求或响应 也可以 被多个 Filter 拦截;
创建一个 Filter 的过程:
1、创建 Filter 处理类;
2、web.xml 文件中配置 Filter;
创建Filter类必须实现 javax.servlet.Filter 接口,在该接口中定义了如下三个方法:
void init(FilterConfig config):用于完成Filter的初始化。
void destroy():用于Filter销毁前,完成某些资源的回收。
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain):
实现过滤功能,该方法就是对,每个请求及响应增加的额外处理;
给出一个日志Filter例子,负责拦截所有的用户请求,并将请求信息记录在日志中:
<span style="white-space:pre"> </span>public class LogFilter implements Filter{
private FilterConfig config;
public void init(FilterConfig config){ this.config = config; }
public void destroy(){ this.config = null; }
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,ServletException<span style="white-space:pre"> </span>{
//======下面代码对用户请求进行预处理========
ServletContext context = this.config.getServletContext();
long before = System.currentTimeMillis();
System.out.println(” 开始过滤 ……“);
HttpServletRequest hrequest = (HttpServletRequest) request; //将请求转换成 HttpServletRequest请求
System.out.println(”Filter 已经截获到用户的请求地址: “ + hrequest.getServletPath());//链式处理,请求依然到目的地址
chain.doFilter(request,response);
//========下面代码对服务器进行后处理=====
long after = System.currentTimeMillis();
System.out.println(” 过滤结束 ……“);
System.out.println(“请求被定位到: ” + hrequest.getRequestURI() + “所花时间为: ” + (after - before));
}
}
在上面请求的Filter中,仅在日志中记录请求的URL,对所有的请求都执行 chaindoFilter(request, response)方法,当 FIlter 对请求过滤后,依然将请求发送到目的地址。如果需要检查权限,可以在Filter 中根据用户请求的 HttpSession ,判断用户权限是否足够。如果权限不够,直接调用重定向即可,无须调用chain.doFilter()方法
配置Filter 跟 Servlet也是差不多:
配置Filter 的名字、配置 Filter 的拦截 URL 模式
区别在于,Servlet 通常只配置一个URL , Filter 可以同时拦截多个请求 的 URL。因此,在配置 Filter 的 URL模式时通常 使用 模式字符串,是的Filter 可以拦截多个请 求。
Filter 也支持两种配置方式 : Annotation配置、 web.xml 配置
主要讲讲 web.xml 配置:
<span style="white-space:pre"> </span><filter>
<filter-name> log </filter-name>
<filter-class> lee.LogFilter </filter-class>
</filter>
<filter-mapping>
<filter-name> log </filter-name>
<url-parttern> /* </url-parttern>
</filter-mapping>
<url-parttern>/* 表示 会拦截所有用户请求。
实际上,Filter 跟 Servlet 极其相似,区别只是 Filter 的 doFilter()方法里多了一个 FIlterChain 的参数,通过该参数可以控制是否放行用户请求。
在实际项目中, FIlter 里 doFilter()方法里的代码就是从多个 Servlet 的 service()方法里抽取的通用代码,通过使用Filter 可以实现更好的代码复用。
假设系统包含多个 Servlet,这些Servlet都需要进行一些通用处理:比如 权限控制、记录日志等,这将导致在这些Servlet 的service()方法中有部分代码是相同的---为了解决这种代码重复的问题,我们可以考虑把这些通用的处理提取到 Filter 中完成,这样个 Servlet 中剩下的只是特定请求相关的处理代码,通用处理就交给 Filter 完成
其实这里给 AOP 做了铺垫,逻辑代码 业务代码 分离!
例如在 web.xml 中配置 Filter,关于 字符编码的问题
</pre><pre name="code" class="html"> <filter>
<filter-name> autority </filter-name>
<filter-class> lee.xxxxx </filter-class>
<init-param>
<param-name> encoding <param-name>
<param-value> GBK <param-value>
</init-param>
<init-param>
<param-name> loginPage <param-name>
<param-value> /login.jsp <param-value>
</init-param>
<init-param>
<param-name> proLogin <param-name>
<param-value> /proLogin <param-value>
</init-param>
<filter>
<filter-mapping>
<filter-name> autoroty </filter-name>
<url-pattern> /* </url-pattern> //负责拦截的URL
</filter-mapping>
上面代码 给 Filter 配置了三个配置参数, 指定 loginPage 为 /login.jsp , proLogin 为 /proLogin.jsp, 这表明,如果没有登录该应用,普通用户只能访问 /login.jsp 和 /proLogin.jsp 页面。只有当用户登录该应用后才可自由访问 其他页面。