将一堆“事情”串联在一起,有序执行,就叫责任链
一、概述
责任链模式(Chain of Responsibility Pattern)是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止,属于行为型模式。下面放一张足球比赛的图,通过层层传递,最终射门。通过这张图,可以更好的理解责任链模式。
二、入门案例
2.1 类图
2.2 基础类介绍
抽象接口 RequestHandler
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 13:41 * @description */public interface RequestHandler {
    void doHandler(String req);}
       复制代码
 
抽象类 BaseRequestHandler
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 13:45 * @description */public abstract class BaseRequestHandler implements RequestHandler {
    protected RequestHandler next;
    public void next(RequestHandler next) {        this.next = next;    }}
       复制代码
 
具体处理类 AHandler
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 14:00 * @description */public class AHandler extends BaseRequestHandler {
    @Override    public void doHandler(String req) {        // 处理自己的业务逻辑        System.out.println("A中处理自己的逻辑");        // 传递给下个类(若链路中还有下个处理类)        if (next != null) {            next.doHandler(req);        }    }}
       复制代码
 当然还有具体的处理类 B、C 等等,这里不展开赘述。
使用类 Client
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 14:06 * @description */public class Client {    public static void main(String[] args) {        BaseRequestHandler a = new AHandler();        BaseRequestHandler b = new BHandler();        BaseRequestHandler c = new CHandler();        a.next(b);        b.next(c);        a.doHandler("链路待处理的数据");    }}
       复制代码
 2.3 处理流程图
三、应用场景
3.1 场景举例
场景一
前两年,在一家金融公司待过一段时间,其中就有一个业务场景:一笔订单进来,会先在后台通过初审人员进行审批,初审不通过,订单流程结束。初审通过以后,会转给终审人员进行审批,不通过,流程结束;通过,流转到下个业务场景。对于这块业务代码,之前一代目是一个叫知了的同事,他撸起袖子就是干,一套 if-else 干到底。后来,技术老大 CodeReview,点名要求改掉这块。于是乎,想到用用设计模式吧,然后就噼里啪啦一顿改。(当然,比较复杂的情况,还是可以用工作流来处理这个场景,当时碍于时间成本,也就放弃了)。
场景二
上家公司对接甲方爸爸的时候,对方会调用我们接口,将数据同步过来。同样,我们需要将处理好的数据,传给他们。由于双方传输数据都是加密传输,所以在接受他们数据之前,需要对数据进行解密,验签,参数校验等操作。同样,我们给他们传数据也需要进行加签,加密操作。
具体案例
话不多说,对于场景二,我来放一些伪代码,跟大家一起探讨下。1、一切从注解开始,我这里自定义了一个注解@Duty,这个注解有 spring 的@Component注解,也就是标记了这个自定义注解的类,都是交给 spring 的 bean 容器去管理。注解中,有两个属性:1.type,定义相同的 type 类型的 bean,会被放到一个责任链集合中。2.order,同一个责任链集合中,bean 的排序,数值越小,会放到链路最先的位置,优先处理。
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 16:11 * @description */@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Servicepublic @interface Duty {    /**     * 标记具体业务场景     * @return     */    String type() default "";
    /**     * 排序:数值越小,排序越前     * @return     */    int order() default 0;}
       复制代码
 
2、定义一个顶层的抽象接口IHandler,传入 2 个泛型参数,供后续自定义。
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 15:31 * @description 责任链顶层抽象类 */public interface IHandler<T, R> {    /**     * 抽象处理类     * @param t     * @return     */    R handle(T t);}
       复制代码
 
3、定义一个责任链 bean 的管理类HandleChainManager,用来存放不同业务下的责任链路集合。在该类中,有一个 Map 和两个方法。
handleMap:这个 map 会存放责任链路中,具体的执行类,key 是注解@Duty中定义的 type 值,value 是标记了@Duty注解的 bean 集合,也就是具体的执行类集合。
setHandleMap:传入具体执行 bean 的集合,存放在 map 中。
executeHandle:从 map 中找到具体的执行 bean 集合,并依次执行。
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 16:00 * @description 责任链管理类 */public class HandleChainManager {    /**     * 存放责任链路上的具体处理类     * k-具体业务场景名称     * v-具体业务场景下的责任链路集合     */    private Map<String, List<IHandler>> handleMap;
    /**     * 存放系统中责任链具体处理类     * @param handlerList     */    public void setHandleMap(List<IHandler> handlerList) {        handleMap = handlerList                .stream()                .sorted(Comparator.comparingInt(h -> AnnotationUtils.findAnnotation(h.getClass(), Duty.class).order()))                .collect(Collectors.groupingBy(handler -> AnnotationUtils.findAnnotation(handler.getClass(), Duty.class).type()));    }
    /**     * 执行具体业务场景中的责任链集合     * @param type 对应@Duty注解中的type,可以定义为具体业务场景     * @param t 被执行的参数     */    public <T, R> R executeHandle(String type, T t) {        List<IHandler> handlers = handleMap.get(type);        R r = null;        if (CollectionUtil.isNotEmpty(handlers)) {            for (IHandler<T, R> handler : handlers) {               r = handler.handle(t);            }        }        return r;    }}
       复制代码
 
4、定义一个配置类PatternConfiguration,用于装配上面的责任链管理器HandleChainManager。
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 15:35 * @description 设计模式配置类 */@Configurationpublic class PatternConfiguration {
    @Bean    public HandleChainManager handlerChainExecute(List<IHandler> handlers) {        HandleChainManager handleChainManager = new HandleChainManager();        handleChainManager.setHandleMap(handlers);        return handleChainManager;    }
}
       复制代码
 
5、具体的处理类:SignChainHandler、EncryptionChainHandler、RequestChainHandler,这里我以SignChainHandler为例。在具体处理类上标记自定义注解@Duty,该类会被注入到 bean 容器中,实现IHandler接口,只需关心自己的 handle 方法,处理具体的业务逻辑。
 /** * @author 往事如风 * @version 1.0 * @date 2022/10/25 15:31 * @description 加签类 */@Duty(type = BusinessConstants.REQUEST, order = 1)public class SignChainHandler implements IHandler<String, String> {    /**     * 处理加签逻辑     * @param s     * @return     */    @Override    public String handle(String s) {        // 加签逻辑        System.out.println("甲方爸爸要求加签");        return "加签";    }}
       复制代码
 
6、具体怎么调用?这里我写了个测试 controller 直接调用,具体如下:
 /** * @author 往事如风 * @version 1.0 * @date 2022/9/6 17:32 * @description */@RestController@Slf4jpublic class TestController {
    @Resource    private HandleChainManager handleChainManager;
    @PostMapping("/send")    public String duty(@RequestBody String requestBody) {        String response = handleChainManager.executeHandle(BusinessConstants.REQUEST, requestBody);        return response;    }}
       复制代码
 
7、执行结果,会按照注解中标记的 order 依次执行。
至此,完工。又可以开心的撸代码了,然后在具体的执行类中,又是一顿 if-else。。。
四、源码中运用
4.1Mybatis 源码中的运用
Mybatis 中的缓存接口Cache,cache 作为一个缓存接口,最主要的功能就是添加和获取缓存的功能,作为接口它有 11 个实现类,分别实现不同的功能,下面是接口源码和实现类。
 package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
public interface Cache {    String getId();
    void putObject(Object var1, Object var2);
    Object getObject(Object var1);
    Object removeObject(Object var1);
    void clear();
    int getSize();
    default ReadWriteLock getReadWriteLock() {        return null;    }}
       复制代码
 
下面,我们来看下其中一个子类LoggingCache的源码。主要看他的 putObject 方法和 getObject 方法,它在方法中直接传给下一个实现去执行。这个实现类其实是为了在获取缓存的时候打印缓存的命中率的。
 public class LoggingCache implements Cache {    private final Log log;    private final Cache delegate;    protected int requests = 0;    protected int hits = 0;
    public LoggingCache(Cache delegate) {        this.delegate = delegate;        this.log = LogFactory.getLog(this.getId());    }
    // ...    public void putObject(Object key, Object object) {        this.delegate.putObject(key, object);    }
    public Object getObject(Object key) {        ++this.requests;        Object value = this.delegate.getObject(key);        if (value != null) {            ++this.hits;        }
        if (this.log.isDebugEnabled()) {            this.log.debug("Cache Hit Ratio [" + this.getId() + "]: " + this.getHitRatio());        }
        return value;    }    // ...}
       复制代码
 
最后,经过Cache接口各种实现类的处理,最终会到达PerpetualCache这个实现类。与之前的处理类不同的是,这个类中有一个 map,在 map 中做存取,也就是说,最终缓存还是会保存在 map 中的。
 public class PerpetualCache implements Cache {    private final String id;    private final Map<Object, Object> cache = new HashMap();
    public PerpetualCache(String id) {        this.id = id;    }
  // ...
    public void putObject(Object key, Object value) {        this.cache.put(key, value);    }
    public Object getObject(Object key) {        return this.cache.get(key);    }  // ...
}
       复制代码
 4.2spring 源码中的运用
4.2.1DispatcherServlet 类
DispatcherServlet 核心方法 doDispatch。HandlerExecutionChain 只是维护 HandlerInterceptor 的集合,可以向其中注册相应的拦截器,本身不直接处理请求,将请求分配给责任链上注册处理器执行,降低职责链本身与处理逻辑之间的耦合程度。
 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {    HttpServletRequest processedRequest = request;    HandlerExecutionChain mappedHandler = null;    boolean multipartRequestParsed = false;    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    try {      ModelAndView mv = null;      Exception dispatchException = null;      try {        processedRequest = checkMultipart(request);        multipartRequestParsed = (processedRequest != request);        // Determine handler for the current request.        mappedHandler = getHandler(processedRequest);        if (mappedHandler == null) {          noHandlerFound(processedRequest, response);          return;        }        // Determine handler adapter for the current request.        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());        // Process last-modified header, if supported by the handler.        String method = request.getMethod();        boolean isGet = "GET".equals(method);        if (isGet || "HEAD".equals(method)) {          long lastModified = ha.getLastModified(request, mappedHandler.getHandler());          if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {            return;          }        }        if (!mappedHandler.applyPreHandle(processedRequest, response)) {          return;        }        // Actually invoke the handler.        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());        if (asyncManager.isConcurrentHandlingStarted()) {          return;        }        applyDefaultViewName(processedRequest, mv);        mappedHandler.applyPostHandle(processedRequest, response, mv);      }      catch (Exception ex) {        dispatchException = ex;      }      catch (Throwable err) {        // As of 4.3, we're processing Errors thrown from handler methods as well,        // making them available for @ExceptionHandler methods and other scenarios.        dispatchException = new NestedServletException("Handler dispatch failed", err);      }      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);    }    catch (Exception ex) {      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);    }    catch (Throwable err) {      triggerAfterCompletion(processedRequest, response, mappedHandler,          new NestedServletException("Handler processing failed", err));    }    finally {      if (asyncManager.isConcurrentHandlingStarted()) {        // Instead of postHandle and afterCompletion        if (mappedHandler != null) {          mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);        }      }      else {        // Clean up any resources used by a multipart request.        if (multipartRequestParsed) {          cleanupMultipart(processedRequest);        }      }    }  }
       复制代码
 4.2.2HandlerExecutionChain 类
这里分析的几个方法,都是从 DispatcherServlet 类的 doDispatch 方法中请求的。
 boolean applyPreHandle(HttpServletRequest request,                        HttpServletResponse response) throws Exception {    HandlerInterceptor[] interceptors = this.getInterceptors();    if (!ObjectUtils.isEmpty(interceptors)) {        for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {            HandlerInterceptor interceptor = interceptors[i];            if (!interceptor.preHandle(request, response, this.handler)) {                this.triggerAfterCompletion(request, response, (Exception)null);                return false;            }        }    }    return true;}
       复制代码
 
 void triggerAfterCompletion(HttpServletRequest request,                             HttpServletResponse response, Exception ex) throws Exception {    HandlerInterceptor[] interceptors = this.getInterceptors();    if (!ObjectUtils.isEmpty(interceptors)) {        for(int i = this.interceptorIndex; i >= 0; --i) {            HandlerInterceptor interceptor = interceptors[i];            try {                interceptor.afterCompletion(request, response, this.handler, ex);            } catch (Throwable var8) {                logger.error("HandlerInterceptor.afterCompletion threw exception", var8);            }        }    }}
       复制代码
 
 void applyPostHandle(HttpServletRequest request,                      HttpServletResponse response, ModelAndView mv)                      throws Exception {    HandlerInterceptor[] interceptors = this.getInterceptors();    if (!ObjectUtils.isEmpty(interceptors)) {        for(int i = interceptors.length - 1; i >= 0; --i) {            HandlerInterceptor interceptor = interceptors[i];            interceptor.postHandle(request, response, this.handler, mv);        }    }}
       复制代码
 五、总结
5.1 优点
将请求与处理解耦。
请求处理者(节点对象)只需要关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,转发给下一个节点。
具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果。
链路结构灵活,可以通过改变链路的结构动态的新增或删减责任。
易于扩展新的请求处理类(节点),符合开闭原则。
5.2 缺点
责任链太长或者处理时间过长,会影响整体性能。
如果节点对象存在循环引用时,会造成死循环,导致系统崩溃。
六、参考源码
 编程文档:https://gitee.com/cicadasmile/butte-java-note
应用仓库:https://gitee.com/cicadasmile/butte-flyer-parent
       复制代码
 
评论