Andy Back-end Dev Engineer

JavaWeb-过滤器


过滤器

Filter是Servlet规范的三大组件之一。顾名思义,就是过滤。可以在请求到达目标资源 之前先对请求进行拦截过滤,即对请求进行一些处理;也可以在响应到达客户端之前先对响应进行拦截过滤,即对响应进行一些处理。 过滤器

Filter生命周期

Filter的生命周期与Servlet的生命周期类似,其主要生命周期阶段有四个:Filter对象的创建、Filter 对象的初始化、Filter执行doFilter()方法,及最终 Filter对象被销毁。Filter的整个生命周期过程的执行,均由 Web服务器负责管理。即 Filter 从创建到销毁的整个过程中方法的调用,都是由 Web 服务器负责调用执行,程序员无法控制其执行流程。 Filter lifecycle

Filter的特征

  • Filter是单例多线程的。
  • Filter是在应用被加载时创建并初始化,这与 Servlet不同的地方。Servlet是在该Servlet被第一次访问时创建。Filter与Servlet的共同点是,其无参构造器与 init()方法只会执行一次。
  • 用户每提交一次该Filter可以过滤的请求,服务器就会执行一次doFilter()方法,即doFilter()方法是可以被多次执行的。
  • 当应用被停止时执行destroy()方法,Filter被销毁,即 destroy()方法只会执行一次。
  • 由于Filter是单例多线程的,所以为了保证线程安全性,一般情况下是不为Filter类定义可修改的成员变量的。因为每个线程均可修改这个成员变量,会出现线程安全问题。

FilterConfig

与 ServletConfig类似,FilterConfig指的是当前 Filter在web.xml中的配置信息。同样是一个Filter对象一个FilterConfig对象,多个Filter对象会有多个FilterConfig 对象。 在Web容器调用init()方法时,Web容器首先会将 web.xml中当前Filter类的配置信息封装为一个FilterConfig对象。Web容器会将这个对象传递给init()方法中的FilterConfig参数。也就是说,我们要获取FilterConfig对象,就需要像 Servlet获取ServletConfig一样,在Filter类中声明一个FilterConfig成员变量,并在init()方法中赋值。

Filter的执行过程

Filter的执行过程 Filter的执行过程2

Servlet的执行原理分析:

两个Map:Web容器存在两个Map,他们的key都是url-pattern的值,第一个map的value为Servlet对象的引用,第二个map的value为<servlet-class>的值。 执行过程: 当Servlet请求到达Servlet容器时,先对请求进行解析,解析出的URI作为比较对象,从第一个map中查找是否有相同的key,如果有,则读取value,执行该servlet的service()方法。 如果在第一个map中没找到,则在第二个map中找,读取到<servlet-class>的值,然后利用反射机制创建servlet实例,并将该实例的引用放入第一个map中,然后执行service()方法。 若第二个map中也没找到则404.

Filter的执行原理分析:

一个Map

像存放Servlet信息的两个Map一样,在服务器中同样存在用于存放Filter相关信息的Map。只不过Map只有一个,而非两个。这个Map的key是Filter的<url-pattern/>。当然,若Filter没有设置<url-pattern/>而是使用了<servlet-name/>,则会将指的Servlet的<url-pattern/>值放到Map中作为key。Map的value为该Filter的引用.在应用被启动时,服务器会自动将所有的Filter实例创建,并将它们的引用放入到相应Map的value中。

一个数组

在服务器中,对于每一个请求,还存在着一个数组,用于存放满足当前请求的所有Filter最终的目标资源。 当请求到达服务器后,服务器会解析出URI,然后会先从Filter的Map中查找所有与该请求匹配的Filter,每找到一个就将其引用存放到数组中,后继承查找。直到将所有匹配的Filter全部找到并添加到数组中。这个数组就是对于当前请求所要进行处理的一个“链”,包含多个Filter。服务器将按照这个“链”的顺序对请求进行依次过滤处理。 注意,我们发现对于Filter的Map的查询过程与对于Servlet的Map的查询过程是不同的。对于Servlet的Map的查询过程是,只要找到一个匹配的key,则将不再向后查找。而对于Filter的Map的查找,则是遍历所有key,将所有匹配的元素都查找出来。

ApplicationFilterChain部分源码解读

public final class ApplicationFilterChain implements FilterChain {
    public static final int INCREMENT = 10;
    /**
     * Filters.
     */
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
    
    /**
     * The int which is used to maintain the current position
     * in the filter chain.
     */
    private int pos = 0;
    
    /**
     * The int which gives the current number of filters in the chain.
     */
    private int n = 0;
    
    /**
     * The servlet instance to be executed by this chain.
     */
    private Servlet servlet = null;
    
    void addFilter(ApplicationFilterConfig filterConfig) {

        // Prevent the same filter being added multiple times
        for(ApplicationFilterConfig filter:filters)
            if(filter==filterConfig)
                return;

        if (n == filters.length) {
            ApplicationFilterConfig[] newFilters =
                new ApplicationFilterConfig[n + INCREMENT];
            System.arraycopy(filters, 0, newFilters, 0, n);
            filters = newFilters;
        }
        filters[n++] = filterConfig;

    }
    
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
        ...
    }
}

装饰者设计模式

Decorator Pattern,能够在不修改目标类也不继承的情况下,动态地扩展一个类的功能。它是通过创建一个包装对象,也就是装饰者来达到增强目标类的目的的。

两个要求:

  • 装饰者类与目标类要实现相同的接口,或继承自相同的抽象类。
  • 装饰者类中要有目标类的引用作为成员变量,而具体的赋值一般通过带参构造器完成。 这两个要求的目的是,在装饰者类中的方法可以调用目标类的方法,以增强这个方法。而增强的这个方法是通过重写的方式进行的增强,所以要求实现相同的接口或继承相同的抽象类。

具体步骤:

  1. 定义业务接口 ISomeService
    // 业务逻辑接口
    public interface ISomeService {
     // 目标方法
     String doSome();
    }
    
  2. 定义目标类 SomeServiceImpl
    // 目标类
    public class SomeServiceImpl implements ISomeService{
    
     @Override
     public String doSome() {
         return "minmin";
     }
    }
    
  3. 定义装饰者基类 SomeServiceWrapper
    // 装饰者基类
    // 要求1: 装饰者要与目标类实现相同的接口或继承相同的类
    // 要求2: 装饰者要有目标类的引用
    // 要求3: 要有无参构造器
    // 要求4: 不对目标方法进行重写
    public class SomeServiecWrapper implements ISomeService{
     // 目标对象
     private ISomeService target;
    	
     public SomeServiecWrapper() {
         super();
     }
    
     // 通过带参构造器传入目标对象
     public SomeServiecWrapper(ISomeService target) {
         this.target = target;
     }
    	
     @Override
     public String doSome() {
         // 仅调用目标类的方法
         return target.doSome();
     }
    }
    
  4. 定义去空格装饰者类 TrimDecorator
    public class TrimDecorator extends SomeServiecWrapper{
    
     public TrimDecorator() {
         super();
     }
    
     public TrimDecorator(ISomeService target) {
         super(target);
     }
    
     @Override
     public String doSome() {
         return super.doSome().trim();
     }
    }
    
  5. 定义小写变大写装饰者类ToUpperCaseDecorator
    public class UpperCaseDecorator extends SomeServiecWrapper{
    	
     public UpperCaseDecorator() {
         super();
     }
    
     public UpperCaseDecorator(ISomeService target) {
         super(target);
     }
    
     @Override
     public String doSome() {
         return super.doSome().toUpperCase();
     }
    }
    
  6. 定义测试类MyTest
    public class MyTest {
     public static void main(String[] args) {
         // 创建目标对象
         ISomeService target = new SomeServiceImpl();
    		
         // 创建装饰者
         ISomeService service = new TrimDecorator(target);
    		
         // 创建装饰者2,形成装饰着琏
         ISomeService service2 = new UpperCaseDecorator(service);
    		
         // 使用装饰者的业务方法
         String result = service2.doSome();
         System.out.println("result: " + result);
     }
    }
    

    装饰者设计模式与静态代理设计模式的对比

    相同点

  7. 装饰者类与目标类要求实现同一接口;静态代理类与目标类要求也实现同一接口。
  8. 装饰者类与静态代理类都可以实现增强目标类的功能。
  9. 装饰者类与静态代理类中都具有目标类的引用,目的都是为了在其中调用目标类的方法。

不同点

  1. 装饰者设计模式就是为了增强目标类;静态代理设计模式是为了保护和隐藏目标对象,让客户类只能访问代理对象,而不能直接访问目标对象。
  2. 装饰者类中的目标类的引用是通过带参构造器传入的;静态代理类中的目标类的引用,一般都是在代理类中直接创建的,目的就是为了隐藏目标对象。
  3. 装饰者基类一般不对目标对象进行增强,而是由不同的具体装饰者进行增强的,且这些具体的装饰者可以形成增强链,对目标对象进行连续增强。静态代理类会直接对目标对象进行增强,需要哪些增强的功能,一次性在静态代理类中完成,没有增强链的概念。

Comments

Content