本篇供个人学习使用,有问题欢迎讨论
Filter基础
Filter 是 Servlet 规范的三大组件之一。顾名思义,就是过滤。可以在请求到达目标资源之前先对请求进行拦截过滤,即对请求进行一些处理;也可以在响应到达客户端之前先对响应进行拦截过滤,即对响应进行一些处理。
一、Filter的生命周期
Filter 的生命周期与 Servlet 的生命周期类似,其主要生命周期阶段有四个:Filter 对象的创建、Filter 对象的初始化、Filter 执行 doFilter() 方法,及最终 Filter 对象被销毁。Filter 的整个生命周期过程的执行,均由 Web 服务器负责管理。即 Filter 从创建到销毁的整个过程中方法的调用,都是由Web服务器负责调用执行,程序员无法控制其执行流程。
1、代码测试
(1)定义 Filter
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//将请求放行到下一个资源
chain.doFilter(req, resp);
}
(2)注册 Filter
/* 表示拦截所有请求
<filter>
<filter-name>OneFilter</filter-name>
<filter-class>com.university.filter.OneFilter</filter-class>
<init-param>
<param-name>university</param-name>
<param-value>qiyuan</param-value>
</init-param>
<init-param>
<param-name>student</param-name>
<param-value>jack</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>OneFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2、Filter 的特征
(1)Filter 是单例多线程的
(2)Filter 是在应用被加载时创建并初始化,这是与 Servlet 不同的地方。Servlet 是在该 Servlet 被第一次访问时创建。Filter 与 Servlet 的共同点是:其无参构造器与 init() 方法只会执行一次
(3)用户每提交一次该 Filter 可以过滤的请求,服务器就会执行一次 doFilter() 方法, 即 doFilter()方法是可以被多次执行的
(4)当应用被停止时执行 destroy() 方法,Filter 被销毁,即 destroy() 方法只会执行一次
(5)由于 Filter 是单例多线程的,所以为了保证其线程安全性,一般情况下是不为 Filter 类定义可修改的成员变量的。因为每个线程均可修改这个成员变量,会出现线程安全问题
二、FilterConfig
1、FilterConfig 的获取
FilterConfig 指的是 Filter 在 web.xml 中的注册信息
2、FilterConfig 中的方法
(1)获取 Filter 的 name
String filterName = filterConfig.getFilterName();
System.out.println("filterName = " + filterName);
(2)获取所有的初始化参数名称
Enumeration<String> names = filterConfig.getInitParameterNames(); //遍历枚举
while (names.hasMoreElements()){
String name = names.nextElement();
String value = filterConfig.getInitParameter(name);
System.out.println(name + "=" + value);
}
(3)获取全局域
ServletContext sc = filterConfig.getServletContext();
System.out.println("ServletContext = " + sc);
控制台输出如下:
三、Filter的<url-pattern/>
1、全路径匹配不支持
若 Filter 为全路径匹配方式,那么 url-pattern 只能写为 /* ,而不能写为 / 。若写成 / ,那么则拦截不了任何页面。
/*:表示当前 Servlet 可以匹配所有的请求,即可以拦截所有的请求,无论发出的是静态资源,还是动态资源的访问请求,统统会被拦截
/:不会拦截动态资源请求,即不会拦截对于 jsp页面的请求,但会拦截静态资源请求
<url-pattern>/*</url-pattern>
2、Filter可以不指定
filter-mapping 中可以不使用 url-pattern,但需要指定 servlet-name,即当前过滤器拦截的是对指定Servlet的请求
<filter-mapping>
<filter-name>OneFilter</filter-name>
<servlet-name>TwoServlet</servlet-name>
</filter-mapping>
四、<dispatcher/>标签
在 <filter-mapping/> 中还有一个子标签 <dispatcher/>,用于设置过滤器所过滤的请求类型。其有四种取值:REQUEST、 FORWARD、INCLUDE、 ERROR.
1、RORWARD
若请求是由一个 Servlet 通过 RequestDispatcher 的 forward() 方法所转发的,那么这个请求将被 值为 FORWARD 的 Filter 拦截。即当前 Filter 只会拦截由RequestDispatcher 的 forward() 方法所转发的请求。其它请求均不拦截。
<filter-mapping>
<filter-name>OneFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
2、INCLUDE
当前 Filter 只会拦截由 RequestDispatcher 的 include() 方法所转发的请求。其它请求均不拦截
<filter-mapping>
<filter-name>OneFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
3、REQUEST
表示当前过滤器会拦截普通请求,但对于 forward() 与 include() 的跳转不进行拦截。默认值
<filter-mapping>
<filter-name>OneFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
4、ERROR
表示当跳转到指定的错误处理页面时,这个跳转请求会被当前过滤器拦截
<filter-mapping>
<filter-name>OneFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
以下为:指定的错误处理页面
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
五、Filter的执行过程
要分析 Filter 的执行过程,需要在项目中定义两个 Filter 与一个 Servlet。它们之间的关系如下图所示。
1、代码测试
OneFilter:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("OneFilter chain.doFilter()之前");
chain.doFilter(req, resp);
System.out.println("OneFilter chain.doFilter()之后");
}
TwoFilter:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("TwoFilter chain.doFilter()之前");
chain.doFilter(req, resp);
System.out.println("TwoFilter chain.doFilter()之后");
}
web.xml:
<filter>
<filter-name>OneFilter</filter-name>
<filter-class>com.university.filter.OneFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OneFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>TwoFilter</filter-name>
<filter-class>com.university.filter.TwoFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TwoFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在浏览器访问 OneServlet后,控制台输出如下:
注意:当应用中存在多个 Filter 时,其执行顺序与其注册顺序一致
2、执行过程分析(重点)
(1)Servlet 的执行原理
两个Map:
Web容器中存在两个 Map,这两个 Map 的 key 均为 Servlet 注册时的 值 ,但其value 是不同的。第一个 Map 的 value 是 Servlet 实例对象的引用,第二个 Map 的 value 为 的值,即 Servlet 类的全限定性类名。
执行原理:
当对 Servlet 的请求到达 Servlet 容器时,会先对请求进行解析,使用解析出的 URI,作为比较对象,从第一个 Map 中查找是否有相同的 key。
若存在相同的 key,那么读取其 value,即 Servlet 对象的引用,执行该 Servlet 的 service()方法。不再向后查找了。
若不存在相同的 key,那么再从第二个 Map 中查找是否有相同的 key。若存在,则读取其 value,即要访问的 Servlet 的全限定性类名。然后使用反射机制创建该 Servlet 实例,并将该实例写入到第一个 Map 中,然后再执行该 Servlet 的 service() 方法。
若第二个 Map 中也没有找到同名的 key,那么跳转到系统错误处理页面404。
(2)Filter 的执行原理
一个数组与一个Map:
一个Map:Map 的 key 为 的值,value 为 Filter 实例对象的引用
一个数组:存放着与请求相匹配的所有 Filter
执行原理:
当对某资源的请求到达 Web 容器时,会先对请求进行解析,使用解析出的 URI 作为比较对象,从 Map 中查找是否存在相匹配的 key。
若存在,那么读取其 value,即 Filter 对象的引用,将该引用存入到数组中。然后继续向后查找,直到将该 Map 查找完毕。这样在数组中就会存在按照查找顺序排好序的 Filter 引用。
数组初始化完毕后,开始按照数组元素顺序进行执行。所有数组中的 Filter 全部执行完毕后,再跳转到请求的目标资源。
3、源码分析之向数组中添加Filter(部分源码)
循环 filter,如果找到相同的 filter,则直接返回。
若没有找到,则判断数组长度是否已满,若已满,则重新新建一个数组,长度加10,并把原来的数组内容复制到新数组中,并把新数组指向 filter,老的数组则被垃圾回收器回收。
若数组长度未满,则把 filter 添加到 filters 数组中,数组里的长度加1。
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;
if(n == filters.1ength) {
ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters,0,newFilters,0,n);
filters = newFilters;
}
filters[n++] = filterConfig;