关注「Java视界」公众号,获取更多技术干货

责任链模式(Chain of Responsibility Pattern)—— 嵌套拦截器

一、what is 责任链模式?

我前面的文章介绍过动态代理模式,该模式设计者只需要向开发者暴露拦截器即可。当有多个拦截器时,就衍生出了责任链。

什么是责任链?比如请假流程:请假单看成对象,你要请假需要经过开发经理、部门经理、人事部门负责人等多个环节,每个环节的人都可以拦截你的请假申请单进行修改或审批,这里把每个环节都看成拦截器的话,总共有三个拦截器。

一个对象在一条链上被多个拦截器处理的模式就是责任链模式,主要用于一个对象在多个角色中传递的场景。

责任链模式一般通过层层代理来实现,也就是说它建立在代理模式的基础上。层代理是什么意思?比如上面的请假流程,开发经理是动态代理1,部门经理是在开发经理动态代理1的基础上生成的动态代理2,人事部门则是在动态代理2基础上生成的动态代理3。如下:

在责任链模式中,多个处理器依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条,链条上的每个处理器 各自承担各自的处理职责。

举个实际项目里的例子,SpringMvc 中可以定义拦截器,并且可以定义多个。当一个用户发起请求时,顺利的话请求会经过所有拦截器,最终到达业务代码逻辑,SpringMvc 拦截器设计就是使用了责任链模式。

二、先看个简单的例子

拦截器1:

public class Iterceptor1 implements Interceptor{
    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器 1 的before()方法");
        return true;
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {

    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器 1 的after()方法");
    }
}

拦截器2:

public class Iterceptor2 implements Interceptor{
    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器 2 的before()方法");
        return true;
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {

    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器 2 的after()方法");
    }
}

拦截器3:

public class Iterceptor3 implements Interceptor{
    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器 3 的before()方法");
        return true;
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {

    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器 3 的after()方法");
    }
}

测试类:

public class DemoTest {
    public static void main(String[] args) {
        BuyHouse proxy1 = (BuyHouse) JDKProxy.getProxy(new FuGuiWang(), "com.wo.domain.proxy.interceptor.Iterceptor1");
        BuyHouse proxy2 = (BuyHouse) JDKProxy.getProxy(proxy1, "com.wo.domain.proxy.interceptor.Iterceptor2");
        BuyHouse proxy3 = (BuyHouse) JDKProxy.getProxy(proxy2, "com.wo.domain.proxy.interceptor.Iterceptor3");
        proxy3.pay();
    }
}
拦截器 3 的before()方法
拦截器 2 的before()方法
拦截器 1 的before()方法
我是王富贵,张狗蛋找的房子我很满意,我要付钱了。
拦截器 1 的after()方法
拦截器 2 的after()方法
拦截器 3 的after()方法

注意执行顺序,before是最后到最前,after是最前到最后。

责任链模式的好处是我们可以在传递链上加上新的拦截器,增加拦截逻辑,很方便实现额外功能,当然缺点也很明,就是或增加反射和代理影响效率。

三、责任链模式处理方式

责任链模式中多个处理器形成的处理器链在进行处理请求时,有两种处理方式:

  1. 请求会被 所有的处理器都处理一遍,不存在中途终止的情况,这里参照 MyBatis 拦截器理解
  2. 二则是处理器链执行请求中,某一处理器执行时,如果不符合自制定规则的话,停止流程,并且剩下未执行处理器就不会被执行,大家参照 SpringMvc 拦截器理解

3.1 中途不终止

首先看下第一种,请求会经过所有处理器执行,中途不终止的情况。 

public interface ValidHandler {
    List<Object> verify(List<Object> allObjectList);
}
public class EmptyValidHandler implements ValidHandler {
    @Override
    public List<Object> verify(List<Object> allObjectList) {
        Console.log("非空验证处理");
        Console.log("模拟检验非空过程");
        return allObjectList;
    }
}
public class LengthValidHandler implements ValidHandler{
    @Override
    public List<Object> verify(List<Object> allObjectList) {
        Console.log("长度验证处理");
        Console.log("模拟检验长度过程");
        return allObjectList;
    }
}
public class RangeValidHandler implements ValidHandler {
    @Override
    public List<Object> verify(List<Object> allObjectList) {
        Console.log("范围验证处理");
        Console.log("模拟检验范围过程");
        return allObjectList;
    }
}
public class ValidHandlerChain {
    List<ValidHandler> handlerList = new ArrayList<>();

    public void addHandlers(List<ValidHandler> handlers){
        handlerList.addAll(handlers);
    }

    public void handle(){
        handlerList.forEach(h->{
            h.verify(new ArrayList<>());
        });
    }
}
public class ChainClient {
    public static void main(String[] args) {
        ValidHandlerChain chain = new ValidHandlerChain();
        chain.addHandlers(Lists.newArrayList(new EmptyValidHandler(), new LengthValidHandler(), new RangeValidHandler()));
        chain.handle();
    }
}
非空验证处理
模拟检验非空过程
长度验证处理
模拟检验长度过程
范围验证处理
模拟检验范围过程

ValidHandlerChain  将处理器串成一条链执行的处理器链(实际就是一个List集合,循环执行里面每个处理器的处理方法)。

这种责任链执行方式会将所有的 处理器全部执行一遍,不会被打断。Mybatis 拦截器用的正是此类型,这种类型 重点在对请求过程中的数据或者行为进行改变。

3.2 停止流程

还有一种责任链模式会对请求有阻断作用,阻断的前置条件是在处理器中自定义的。

public interface ValidHandler {
    boolean verify(List<Object> allObjectList);
}
public class EmptyValidHandler implements ValidHandler {
    @Override
    public boolean verify(List<Object> allObjectList) {
        Console.log("非空验证处理");
        Console.log("模拟检验非空过程");
        return true;
    }
}
public class LengthValidHandler implements ValidHandler {
    @Override
    public boolean verify(List<Object> allObjectList) {
        Console.log("长度验证处理");
        Console.log("模拟检验长度过程");
        return false;
    }
}
public class RangeValidHandler implements ValidHandler {
    @Override
    public boolean verify(List<Object> allObjectList) {
        Console.log("范围验证处理");
        Console.log("模拟检验范围过程");
        return true;
    }
}
public class ValidHandlerChain {
    List<ValidHandler> handlerList = new ArrayList<>();

    public void addHandlers(List<ValidHandler> handlers) {
        handlerList.addAll(handlers);
    }

    public boolean handle() {
        boolean flag = true;
        for (ValidHandler handler : handlerList) {
            boolean isSuccess = handler.verify(new ArrayList<>());
            if (!isSuccess) {
                flag = isSuccess;
                break;
            }
        }
        return flag;
    }
}
public class ChainClient {
    public static void main(String[] args) {
        ValidHandlerChain chain = new ValidHandlerChain();
        chain.addHandlers(Lists.newArrayList(new EmptyValidHandler(), new LengthValidHandler(), new RangeValidHandler()));
        boolean flag = chain.handle();
        if(!flag){
            Console.log("中间有处理器处理错误,中断责任链流程");
        }
    }
}
非空验证处理
模拟检验非空过程
长度验证处理
模拟检验长度过程
中间有处理器处理错误,中断责任链流程

在每一个 ValidHandler 实现类中会返回一个布尔类型的返回值,如果返回布尔值为 false,那么责任链发起类会中断流程,剩余处理器将不会被执行。就像我们定义在 SpringMvc 中的 Token 拦截器,如果 Token 失效就不能继续访问系统,处理器将请求打回。

 

四、责任链模式的优点及缺点

优点:

  • 对象仅需知道该请求会被处理即可,且链中的对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度

  • 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接

  • 在给对象分派职责时,职责链可以给我们更多的灵活性,可以在运行时对该链进行动态的增删改,改变处理一个请求的职责

  • 新增一个新的具体请求处理者时无须修改原有代码,只需要在客户端重新建链即可,符合 "开闭原则"

缺点:

  • 一个请求可能因职责链没有被正确配置而得不到处理

  • 对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,且不方便调试

  • 可能因为职责链创建不当,造成循环调用,导致系统陷入死循环

适用场景:

  • 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的

  • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求

  • 可动态指定一组对象处理请求,客户端可以动态创建职责链来处理请求,还可以改变链中处理者之间的先后次序

posted @ 2022-06-25 14:02  沙滩de流沙  阅读(167)  评论(0编辑  收藏  举报

关注「Java视界」公众号,获取更多技术干货