Filter基础讲解篇

本篇供个人学习使用,有问题欢迎讨论

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;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值