聊一聊责任链模式(责任链模式的概念)

  本篇文章为你整理了聊一聊责任链模式(责任链模式的概念)的详细内容,包含有责任链模式属于什么模式 责任链模式的概念 责任链模式实例 责任链模式好处 聊一聊责任链模式,希望能帮助你了解 聊一聊责任链模式。

   责任链模式(Chain of Responsibility Pattern)是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。

  
责任链模式(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

  @Service

  public @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 设计模式配置类

  @Configuration

  public 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

  @Slf4j

  public 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, were 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 --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 --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

  

 

  以上就是聊一聊责任链模式(责任链模式的概念)的详细内容,想要了解更多 聊一聊责任链模式的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: