最近在学习Spring security框架过程中发现,security是一个基于过滤器拦截器实现的框架。它内置了十几个过滤器(看的头大)。所以说过滤器在这个框架中扮演了不可或缺的角色。但我已经本身很少写过滤器了,于是就抽时间复习了一下过滤器。在复习过程中我发现过滤器链的执行类似于一个净水器,你发的请求就像不干净的水,然后净水器中的每一环都会对水进行过滤,其中有过滤金属的,过滤有害物质的等等,这些过滤组件依照一定的顺序组合起来使用就能达到很好的效果。出于好奇上网搜索了一下,得知过滤器的执行逻辑就是基于:责任链模式。
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。它可以使数据的发送者和接收者解耦,数据沿着责任链传递,直到有一个对象处理了它为止。
责任链模式有两种实现方式:一种基于单链表实现,一种依靠集合实现。第一种比较好理解,第二种使用的更广泛,FilterChain也是使用这种方式。光看说明可能会有点懵,上代码。
首先我们需要一个要被处理的数据,这个数据在实际开发中有可能是请求或是其他的。
public interface DataResource {
void doSomething();
}
public class DataResourceImpl implements DataResource{
//业务数据
private Integer data = 0;
@Override
public void doSomething() {
data++;
}
public Integer getData(){
return data;
}
}
一、单链表方式:
当然,用单链表实现的前提需要搞懂单链表这个简单的数据结构~
public abstract class AbstractHandler {
private AbstractHandler next;
/**
* 执行逻辑
*/
public abstract void handleRequest(DataResource dataResource);
public AbstractHandler getNext(){
return next;
}
public void setNext(AbstractHandler next){
this.next = next;
}
}
定义一个抽象类,我定义的过滤节点都会继承它。
public class Handler extends AbstractHandler{
@Override
public void handleRequest(DataResource dataResource) {
dataResource.doSomething();
}
}
实现类重写处理方法。
public class StrongerHandler extends AbstractHandler{
@Override
public void handleRequest(DataResource dataResource) {
for (int i = 0; i < 5; i++) {
dataResource.doSomething();
}
}
}
再搞一个实现类,他会对被处理类的方法执行5次。
public class LinkHandlerUtil {
private static AbstractHandler head = new Handler();
/**
* 功能描述 :遍历链表添加节点
* @author HKH
* @date
* @param
* @return
*/
public static void addHandler(AbstractHandler handler){
AbstractHandler temp = head;
while(true){
if(temp.getNext()==null){
temp.setNext(handler);
break;
}
temp = temp.getNext();
}
}
/**
* 功能描述 :依次执行所有节点
* @author HKH
* @date
* @param
* @return
*/
public static void HandleAll(DataResource dataResource){
AbstractHandler temp ;
if(head.getNext()==null){
System.out.println("这是个空链表,请先添加节点。");
return;
}
temp = head.getNext();
while (true){
if(temp==null){
System.out.println("所有节点执行完毕,退出。");
break;
}
temp.handleRequest(dataResource);
temp = temp.getNext();
}
}
}
由于我没有在过滤节点处实现链表式的调用,所以我这里写了一个工具类,该类包含对过滤链的添加和调用方法。
public class Test {
public static void main(String[] args) {
AbstractHandler handler = new Handler();
AbstractHandler strong = new StrongerHandler();
LinkHandlerUtil.addHandler(handler);
LinkHandlerUtil.addHandler(strong);
DataResourceImpl dataResource = new DataResourceImpl();
LinkHandlerUtil.HandleAll(dataResource);
System.out.println(dataResource.getData());
}
}
经测试结果为 6.
二、通过处理器集合来定义处理顺序:
这种方法的好处就是可以集中维护所有节点。
/**
* @program: JavaSe
* @description: 责任链接口,对责任链进行抽象
* @author: HKH
* @create: 2019-11-28 14:43
**/
public interface HandlerChain {
/**
* 功能描述 :增加责任链节点
* @author HKH
* @date
* @param
* @return
*/
void addHandler(Handler handler);
/**
* 功能描述 :执行责任链
* @author HKH
* @date
* @param
* @return
*/
void doChain(DataResource dataResource);
}
对过滤器链进行抽象,其拥有增加节点和执行方法。
public class ListHandlerChainImpl implements HandlerChain{
//当前handler的指针
private Integer index = 0;
//存放handler的集合
private List<Handler> handlers = new ArrayList<>();
@Override
public void addHandler(Handler handler) {
handlers.add(handler);
}
@Override
public void doChain(DataResource dataResource) {
int size = handlers.size();
if(index<size){
Handler handler = handlers.get(index++);
handler.doHandler(dataResource,this);
}
}
}
ListHandlerChainImpl 负责维护调用链条的顺序,这里实现用List来管理Handler。不过请注意这个Handler跟我之前定义的Handler有所不同,继续往下看。
public interface Handler{
void doHandler(DataResource dataResource, HandlerChain handlerChain);
}
public class HandlerImpl implements Handler{
@Override
public void doHandler(DataResource dataResource, HandlerChain handlerChain) {
//处理业务
DataResourceImpl dataResourceImpl = (DataResourceImpl) dataResource;
dataResourceImpl.doSomething();
handlerChain.doChain(dataResource);
}
}
根据代码我们可以看出,该Handler可以决定是否继续向下执行,是不是和我们学过的过滤器越来越像了!而且处理完方法后会调回到HandlerChain处继续遍历下一个节点。我们来测试一下。
public class Test {
public static void main(String[] args) {
HandlerChain handlerChain = new ListHandlerChainImpl();
handlerChain.addHandler(new HandlerImpl());
handlerChain.addHandler(new HandlerImpl());
DataResourceImpl dataResource = new DataResourceImpl();
handlerChain.doChain(dataResource);
System.out.println(dataResource.getData());
}
}
测试结果为 2.
小总结:
优点:
- 责任链模式降低了请求发送者和处理者的耦合度。
- 提高系统的灵活性。
缺点:
- 第一种方式不灵活,每次都要从头遍历到尾。
- 第二种方式类似于递归调用,逻辑有些复杂。