过滤器介绍
过滤器是Servlet的高级特性之一,也别把它想得那么高深,只不过是实现Filter接口的Java类罢了!
从上面的图可以发现,当浏览器发送请求给服务器的时候,先执行过滤器,然后才访问Web的资源。服务器响应Response,从Web资源抵达浏览器之前,也会途径过滤器。
过滤器可以做什么?
答:过滤一些敏感的字符串【规定不能出现敏感字符串】、避免中文乱码【规定Web资源都使用UTF-8编码】、权限验证【规定只有带Session或Cookie的浏览器,才能访问web资源】等等等,过滤器的作用非常大,只要发挥想象就可以有意想不到的效果。
总结:当需要限制用户访问某些资源时、在处理请求时提前处理某些资源、服务器响应的内容对其进行处理再返回、我们就是用过滤器来完成的!
为什么要用过滤器?
这里用解决中文乱码为例。
1、如果没有用过滤器的话,发送http请求到 servlet 时,中文乱码是需要在代码中设定编码来解决的。
2、如果使用过滤器的话,只要我在过滤器中指定了编码,可以使全站的Web资源都是使用该编码,并且重用性是非常理想的!
过滤器 API
只要Java类实现了Filter接口就可以称为过滤器!
其中init()和destory()方法就不用多说了,他俩跟Servlet是一样的。只有在Web服务器加载和销毁的时候被执行,只会被执行一次!
值得注意的是doFilter()方法,它有三个参数(ServletRequest,ServletResponse,FilterChain),从前两个参数我们可以发现:过滤器可以完成任何协议的过滤操作!
过滤器不单单只有一个,那么我们怎么管理这些过滤器呢?
答:在Java中就使用了链式结构。把所有的过滤器都放在FilterChain里边,如果符合条件,就执行下一个过滤器(如果没有过滤器了,就执行目标资源)。
使用过滤器
1、实现Filter接口的Java类就被称作为过滤器
public class FilterDemo1 implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//执行这一句,说明放行(让下一个过滤器执行,如果没有过滤器了,就执行执行目标资源)
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
2、过滤器部署
过滤器和Servlet是一样的,需要部署到Web服务器上的。
第一种方式:在web.xml文件中配置
1、<filter>用于注册过滤器
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>FilterDemo1</filter-class>
<init-param>
<param-name>word_file</param-name>
<param-value>/WEB-INF/word.txt</param-value>
</init-param>
</filter>
- <filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
- <filter-class>元素用于指定过滤器的完整的限定类名。
- <init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,
- <param-value>指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
2、<filter-mapping>元素用于设置一个Filter 所负责拦截的资源。
一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- <filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字
- <url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
- <servlet-name>指定过滤器所拦截的Servlet名称。
- <dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
3、<dispatcher> 子元素可以设置的值及其意义:
- REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
- INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
- FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
- ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
第二种方式:通过注解配置
@WebFilter(filterName = "FilterDemo1",urlPatterns = "/*")
上面的配置是“/*”,所有的Web资源都需要途径过滤器
如果想要部分的Web资源进行过滤器过滤则需要指定Web资源的名称即可!
3、过滤器的执行顺序
首先执行一个案例:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("准备放行");
//执行这一句,说明放行(让下一个过滤器执行,或者执行目标资源)
chain.doFilter(req, resp);
System.out.println("放行完成");
}
它的完整流程顺序是这样的:客户端发送http请求到Web服务器上,Web服务器执行过滤器,执行到”准备放行“时,就把字符串输出到控制台上,接着执行doFilter()方法,Web服务器发现没有过滤器了,就执行目标资源(也就是test.jsp)。目标资源执行完后,回到过滤器上,继续执行代码,然后输出”放行完成“
再看一个案例,写两个过滤器:
过滤器1
System.out.println("过滤器1开始执行");
//执行这一句,说明放行(让下一个过滤器执行,或者执行目标资源)
chain.doFilter(req, resp);
System.out.println("过滤器1开始完毕");
过滤器2
System.out.println("过滤器2开始执行");
chain.doFilter(req, resp);
System.out.println("过滤器2开始完毕");
servlet
System.out.println("我是Servlet1");
当我们访问Servlet1的时候,看看控制台会出现什么:
执行顺序是这样的:先执行FilterDemo1,放行,执行FilterDemo2,放行,执行Servlet1,Servlet1执行完回到FilterDemo2上,FilterDemo2执行完毕后,回到FilterDemo1上
**注意:**过滤器之间的执行顺序看在web.xml文件中mapping的先后顺序的,如果放在前面就先执行,放在后面就后执行!如果是通过注解的方式配置,就比较urlPatterns的字符串优先级
filter的三种典型应用
1、可以在filter中根据条件决定是否调用chain.doFilter(request, response)方法,即是否让目标资源执行
2、在让目标资源执行之前,可以对requestresponse作预处理,再让目标资源执行
3、在目标资源执行之后,可以捕获目标资源的执行结果,从而实现一些特殊的功能