过滤器
Filter是Servlet规范的三大组件之一。顾名思义,就是过滤。可以在请求到达目标资源 之前先对请求进行拦截过滤,即对请求进行一些处理;也可以在响应到达客户端之前先对响应进行拦截过滤,即对响应进行一些处理。
Filter生命周期
Filter的生命周期与Servlet的生命周期类似,其主要生命周期阶段有四个:Filter对象的创建、Filter 对象的初始化、Filter执行doFilter()方法,及最终 Filter对象被销毁。Filter的整个生命周期过程的执行,均由 Web服务器负责管理。即 Filter 从创建到销毁的整个过程中方法的调用,都是由 Web 服务器负责调用执行,程序员无法控制其执行流程。
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的执行过程
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,能够在不修改目标类也不继承的情况下,动态地扩展一个类的功能。它是通过创建一个包装对象
,也就是装饰者
来达到增强目标类的目的的。
两个要求:
- 装饰者类与目标类要实现相同的接口,或继承自相同的抽象类。
- 装饰者类中要有目标类的引用作为成员变量,而具体的赋值一般通过带参构造器完成。
这两个要求的目的是,在装饰者类中的方法可以调用目标类的方法,以增强这个方法。而增强的这个方法是通过
重写
的方式进行的增强,所以要求实现相同的接口或继承相同的抽象类。
具体步骤:
- 定义业务接口 ISomeService
// 业务逻辑接口 public interface ISomeService { // 目标方法 String doSome(); }
- 定义目标类 SomeServiceImpl
// 目标类 public class SomeServiceImpl implements ISomeService{ @Override public String doSome() { return "minmin"; } }
- 定义装饰者基类 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(); } }
- 定义去空格装饰者类 TrimDecorator
public class TrimDecorator extends SomeServiecWrapper{ public TrimDecorator() { super(); } public TrimDecorator(ISomeService target) { super(target); } @Override public String doSome() { return super.doSome().trim(); } }
- 定义小写变大写装饰者类ToUpperCaseDecorator
public class UpperCaseDecorator extends SomeServiecWrapper{ public UpperCaseDecorator() { super(); } public UpperCaseDecorator(ISomeService target) { super(target); } @Override public String doSome() { return super.doSome().toUpperCase(); } }
- 定义测试类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); } }
装饰者设计模式与静态代理设计模式的对比
相同点
- 装饰者类与目标类要求实现同一接口;静态代理类与目标类要求也实现同一接口。
- 装饰者类与静态代理类都可以实现增强目标类的功能。
- 装饰者类与静态代理类中都具有目标类的引用,目的都是为了在其中调用目标类的方法。
不同点
- 装饰者设计模式就是为了增强目标类;静态代理设计模式是为了保护和隐藏目标对象,让客户类只能访问代理对象,而不能直接访问目标对象。
- 装饰者类中的目标类的引用是通过带参构造器传入的;静态代理类中的目标类的引用,一般都是在代理类中直接创建的,目的就是为了隐藏目标对象。
- 装饰者基类一般不对目标对象进行增强,而是由不同的具体装饰者进行增强的,且这些具体的装饰者可以形成增强链,对目标对象进行连续增强。静态代理类会直接对目标对象进行增强,需要哪些增强的功能,一次性在静态代理类中完成,没有增强链的概念。