将一堆“事件”串联在一起,有序执行,就叫责任链

一、概述

责任链模式(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和两个办法。

  1. handleMap:这个map会寄存责任链路中,具体的执行类,key是注解@Duty中定义的type值,value是标记了@Duty注解的bean汇合,也就是具体的执行类汇合。
  2. setHandleMap:传入具体执行bean的汇合,寄存在map中。
  3. 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、具体的解决类:SignChainHandlerEncryptionChainHandlerRequestChainHandler,这里我以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办法中申请的。

  • 获取拦截器,执行preHandle办法

    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;}
  • 在applyPreHandle办法中,执行triggerAfterCompletion办法

    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);          }      }  }}
  • 获取拦截器,执行applyPostHandle办法

    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 长处

  1. 将申请与处了解耦。
  2. 申请解决者(节点对象)只须要关注本人感兴趣的申请进行解决即可,对于不感兴趣的申请,转发给下一个节点。
  3. 具备链式传递解决申请性能,申请发送者无需通晓链路构造,只需期待申请处理结果。
  4. 链路构造灵便,能够通过扭转链路的构造动静的新增或删减责任。
  5. 易于扩大新的申请解决类(节点),合乎开闭准则

    5.2 毛病

  6. 责任链太长或者解决工夫过长,会影响整体性能。
  7. 如果节点对象存在循环援用时,会造成死循环,导致系统解体。

    六、参考源码

    编程文档:https://gitee.com/cicadasmile/butte-java-note利用仓库:https://gitee.com/cicadasmile/butte-flyer-parent