Servlet中过滤器的实现原理

一 回顾

控制台有如下打印:

过滤器1执行前
过滤器2执行前
过滤器3执行前
过滤器3执行后
过滤器2执行后
过滤器1执行后

很明显这是使用了三个过滤器。并且每个过滤器都执行了doFilter方法,并且在doFilter方法的执行前后都打印了一句话。所以才会有上述现象。

总结一下过滤器特点:

①第一个过滤器一定会执行。

②下个过滤器能否执行取决于上一个过滤器是否放行。所谓的放行就是调用doFilter方法。

③所有的过滤器都会以链条的形式存在,如果最后一个过滤器得不到执行。那么被拦截的目标(Servlet)也不会得到执行。

二 想法和实现

这里有两个、也只有两个核心的概念:过滤器链和过滤器。两者的关系是容器和元素之间的包含关系。

扫描二维码关注公众号,回复: 1476196 查看本文章

过滤器链是个容器,可以将它理解称一个数组、集合都没问题。过滤器就是一个类。

到这里,我们可以画出一张图。

这张图对于过滤器理解工作原理来说是没问题的。绿色的√表示过滤器放行,红色的×表示得不到执行的机会。然而要想知道具体实现的细节。我们需要变动一下,

别忘了控制台打印出来的文字是:嵌套的,是嵌套的,嵌套的!!!!

嵌套:意味着递归!

所以正确的过滤器链的执行流程应该是"回型"结构的。像这样:

现在我们把问题模型给简化,把和过滤器不相关的,不重要的东西全部丢掉。

我们定义如下几个接口:

public interface FilterChain {
	public void execute();
}
public interface Filter {
	public void doFilter(FilterChain chain);
}

再定义一下几个过滤器实现类

public class FilterImpl1 implements Filter{
	@Override
	public void doFilter(FilterChain chain) {
		System.out.println("过滤器1执行前");
			chain.execute();
		System.out.println("过滤器1执行后");
	}
}
public class FilterImpl2 implements Filter{
	@Override
	public void doFilter(FilterChain chain) {
		System.out.println("过滤器2执行前");
			chain.execute();
		System.out.println("过滤器2执行后");
	}
}
public class FilterImpl3 implements Filter{
	@Override
	public void doFilter(FilterChain chain) {
		System.out.println("过滤器3执行前");
			chain.execute();
		System.out.println("过滤器3执行后");
	}
}

假设配置的执行顺序是1-->2-->3。如何才能实现本文开头提到的控制台的嵌套打印效果呢?

如果说把过滤器比作多米诺骨牌,那么谁来推倒第一张牌?过滤器链的实现类。

谁来负责连续推倒下一张牌?  表面上看是由开发者在doFilter中是否调用chain.execute();决定。然而

最终开发者调用的是过滤器链的execute方法。所以到这里,我们可以得出,过滤器链要实现的功能如下:

1 保存所有的过滤器对象。

2 在execute方法中,取出下一个过滤器对象。执行doFilter方法。实现递归。

这样就能确保,如果开发者在当前过滤器的doFilter方法中不手动调用chain.execute();方法。那么拦截的目标是一定不会执行。并且下一个过滤器也一定不会得到执行

到这里,思路其实已经很明了了。最关键的代码在FilterChain的实现类的中。也就是我们需要记录是否还有下一个过滤器。

这里提供两种实现方式:计数器方式和移除方式。

计数器方式实现的过滤器链:

public class FilterChainImpl implements FilterChain{
	List<Filter> listfilter = new ArrayList<Filter>();
	private int index = 0;
	public void append(Filter filter){
		listfilter.add(filter);
	}
	private boolean execute_target = false;
	@Override
	public void execute() {
		if(index<listfilter.size()){
			Filter filter = listfilter.get(index);
			index++;
			filter.doFilter(this);
		} 
		if(index==listfilter.size()){
			if(execute_target == false){
				execute_target = true;
				System.out.println("执行拦截目标的方法");
			}
		}
	}
}

移除方式实现的过滤器链:

public class FilterChainImpl implements FilterChain{
	LinkedList<Filter> listfilter = new LinkedList<Filter>();
	public void append(Filter filter){
		listfilter.add(filter);
	}
	@Override
	public void execute() {
	   if(hasNextFilter())
		   nextFilter().doFilter(this);
	   else{
		  System.out.println("执行目标方法");
	   }
	}
	public boolean hasNextFilter(){
		return listfilter.size() > 0;
	}
	public Filter nextFilter(){
		return listfilter.pop();
	}
}

测试类:

public class TestFilterDemo {
	public static void main(String[] args) {
		FilterChainImpl chain = new FilterChainImpl();
		chain.append(new FilterImpl1());
		chain.append(new FilterImpl2());
		chain.append(new FilterImpl3());
		chain.execute();
	}
}

三 完整代码

一共有以下几个类:

Filter  接口。实际中Servlet的过滤器还有其它方法,但是比较简单。这里就不写了。下同。

FilterChain 接口。之前出给出的execute方法。是为了区别于Filter的doFilter方法。只要理解了原理,方法名是什么不重要。

FilterChainImpl1 FilterChain的子类。采用计数器方式实现过滤器链。

FilterChainImpl2 FilterChain的子类。采用移除方式实现过滤器链。

FilterImpl1~FilterImpl3  三个Filter的实现类。

TestFilterDemo 测试来,含有main函数。

request对象和Response对象。这里没有给出来。也不需要给。

public interface Filter {
	public void doFilter(Request rq,Response rp,FilterChain chain);
}
public interface FilterChain {
	public void doFilter(Request rq,Response rp);
}
public class FilterImpl1 implements Filter{
	@Override
	public void doFilter(Request rq,Response rp,FilterChain chain) {
		System.out.println("过滤器1执行前");
			chain.doFilter(rq, rp);
		System.out.println("过滤器1执行后");
	}
}
public class FilterImpl2 implements Filter{
	@Override
	public void doFilter(Request rq,Response rp,FilterChain chain) {
		System.out.println("过滤器2执行前");
			 chain.doFilter(rq, rp);
		System.out.println("过滤器2执行后");
	}
}
public class FilterImpl3 implements Filter{
	@Override
	public void doFilter(Request rq,Response rp,FilterChain chain) {
		System.out.println("过滤器3执行前");
			chain.doFilter(rq, rp);
		System.out.println("过滤器3执行后");
	}
}
public class FilterChainImpl1 implements FilterChain{
	private LinkedList<Filter> listfilter = new LinkedList<Filter>();
	public void append(Filter filter){
		listfilter.add(filter);
	}
	@Override
	public void doFilter(Request rq,Response rp) {
	   if(hasNextFilter())
		   nextFilter().doFilter(rq,rp,this);
	   else{
		  System.out.println("执行目标方法");
	   }
	}
	public boolean hasNextFilter(){
		return listfilter.size() > 0;
	}
	public Filter nextFilter(){
		return listfilter.pop();
	}
}
public  class FilterChainImpl2 implements FilterChain{
	private List<Filter> listfilter = new ArrayList<Filter>();
	private int index = 0;
	public void append(Filter filter){
		listfilter.add(filter);
	}
	private boolean execute_target = false;
	@Override
	public void doFilter(Request rq,Response rp) {
		if(index<listfilter.size()){
			Filter filter = listfilter.get(index);
			index++;
			filter.doFilter(rq,rp,this);
		} 
		if(index==listfilter.size()){
			if(execute_target == false){
				execute_target = true;
				System.out.println("执行拦截目标的方法");
			}
		}
	}
}
public class TestFilterDemo {
	public static void main(String[] args) {
		FilterChainImpl1 chain = new FilterChainImpl1();
		chain.append(new FilterImpl1());
		chain.append(new FilterImpl2());
		chain.append(new FilterImpl3());
       //这里实际会传入rquest和response对象。现在只能不传,但是不影响过滤器的是实现。
		chain.doFilter(null,null);
		 
	}
}

猜你喜欢

转载自my.oschina.net/lightled/blog/1824766