过滤器(Filter)
1. 什么是过滤器
过滤器是servlet规范当中定义的一种特殊的组件,用来拦截servlet容器的调用过程。
当servlet容器收到请求之后,如果有过滤器,会先调用过滤器
过滤器一般用于登录权限验证、资源访问权限控制、敏感词汇过滤、字符编码转换等等操作,便于代码重用,不必每个servlet中还要进行相应的操作。
2. 过滤器是如何实现拦截的
Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
- 调用目标资源之前,让一段代码执行。
- 是否调用目标资源(即是否让用户访问web资源)。
- 调用目标资源之后,让一段代码执行。
web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对 象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方 法,即web资源就会被访问,否则web资源不会被访问。
3. 如何写过滤器
3.1 写一个java类,实现Filter接口
public class CommentFilter implements Filter {
/**
* 容器启动之后,会立即将过滤器实例化
* 只会创建一个实例
*/
public CommentFilter() {
System.out.println("CommentFilter的构造器");
}
/**
* 容器在删除过滤器实例之前,会调用destroy方法
* 该方法只会执行一次
*/
public void destroy() {
System.out.println("commentFilter的destroy方法");
}
/**
* 容器会调用doFilter放来来处理请求(类似于service方法)
* FilterChain(过滤器链):如果调用了该对象的doFilter方法,表示继续向后执行,
* 否则中断请求,返回处理结果。
*
* ServletRequest是HttpServletRequest的父接口
* ServletResponse是HttpServletResponse的父接口
* 这儿使用父接口是因为这是sun公司过度设计的产物
*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
System.out.println("CommentFilter的doFilter方法开始执行...");
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session=request.getSession();
String path=req.getRequestURI();
Integer uid=(Integer)session.getAttribute("userid");
if(path.indexOf("/login.jsp")>-1){//登录页面不过滤
chain.doFilter(req, res);//递交给下一个过滤器
return;
}
if(path.indexOf("/register.jsp")>-1){//注册页面不过滤
chain.doFilter(req, res);
return;
}
if(uid!=null){//已经登录
chain.doFilter(req, res);//放行,递交给下一个过滤器
}else{
response.sendRedirect("login.jsp");
}
System.out.println("CommentFilter的doFilter方法执行完毕");
}
/**
* 容器在创建好过滤器实例之后会调用该实例的Init方法
* 该方法只会执行一次
* FilterConfig对象类似于ServletConfig,可以用来读取初始化参数
*/
public void init(FilterConfig arg0) throws ServletException {
System.out.println("CommentFilter的Init方法");
}
}
3.2 配置过滤器(web.xml)
<!--配置过滤器-->
<filter>
<filter-name>commentFilter</filter-name>
<filter-class>web.CommentFilter</filter-class>
</filter>
<!--映射过滤器-->
<filter-mapping>
<filter-name>commentFilter</filter-name>
<url-pattern>/comment</url-pattern>
</filter-mapping>
配置过滤器一般有以下规则:
-
作用与所有web资源:<url—pattern>/*</url-pattern>。则客户端请求访问任意资源文件时都要经过过滤器过滤,通过则访问文件,否则拦截
-
作用于某一文件夹下所有文件:<url—pattern>/dir/*</url-pattern>
-
作用于某一种类型的文件:<url—pattern>.扩展名</url-pattern>。
比如<url—pattern>.jsp</url-pattern>过滤所有对jsp文件的访问请求
-
作用于某一文件夹下某一类型文件:<url—pattern>/dir/*.扩展名</url-pattern>
4. 过滤器的优先级(Filter链)
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
如图,浏览器发出的请求先递交给第一个filter进行过滤,符合规则则放行,递交给filter链中的下一个过滤器进行过滤。过滤器在链中的顺序与它在web.xml中配置的顺序有关,配置在前的则位于链的前端。当请求通过了链中所有过滤器后就可以访问资源文件了,如果不能通过,则可能在中间某个过滤器中被处理掉。
**在doFilter()方法中,chain.doFilter()前的一般是对request执行的过滤操作,chain.doFilter后面的代码一般是对response执行的操作。**过滤链代码的执行顺序如下:
5. 过滤器的生命周期
实例化 --> 初始化 --> 处理拦截 --> 销毁
注意:init方法也只会执行一次,destroy方法也只会执行一次
5.1 Filter的创建(实例化)
/**
* 容器启动之后,会立即将过滤器实例化
* 只会创建一个实例
*/
public CommentFilter() {
System.out.println("CommentFilter的构造器");
}
5.2 Filter调用Init()方法(初始化)
/**
* 容器在创建好过滤器实例之后会调用该实例的Init方法
* 该方法只会执行一次
* FilterConfig对象类似于ServletConfig,可以用来读取初始化参数
*/
public void init(FilterConfig arg0) throws ServletException {
System.out.println("CommentFilter的Init方法");
}
5.3 Filter方法执行(处理拦截)
/**
* 容器会调用doFilter放来来处理请求(类似于service方法)
* FilterChain(过滤器链):如果调用了该对象的doFilter方法,表示继续向后执行,
* 否则中断请求,返回处理结果。
*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
System.out.println("CommentFilter的doFilter方法开始执行...");
...
System.out.println("CommentFilter的doFilter方法执行完毕");
}
5.4 Filter调用destroy()方法(销毁)
/**
* 容器在删除过滤器实例之前,会调用destroy方法
* 该方法只会执行一次
*/
public void destroy() {
System.out.println("commentFilter的destroy方法");
}