责任链模式

mac2022-06-30  21

定义

使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止

使用场景

如果一个请求可能会出现多个或未知个数处理器实例,或者请求处理器可动态配置的情况下,这时候便可使用责任链模式。 几乎所有的开源框架中都使用到该模式,如 Spring 中的拦截器、过滤器,通过 ant 表达式风格的 url 参数来判断是否对请求进行拦截,Netty 中 ChannelPipeLine 通过链表的形式添加 ChannelHandler 处理节点。

在SpringBoot中使用

结构图

责任链的处理核心在"链"(Chain)上面。"链"是由多个处理者 ConcreateX 组成的,我们先来看抽象 “Handler” 接口:

/** * @author tianp **/ public interface Handler { /** * 是否支持处理 * * @param uri 匹配uri * @return true 成功 false 失败 */ boolean support(String uri); /** * 处理 * * @param requestBody 处理参数封装 * @return true 成功 false 失败 */ boolean handle(RequestBody requestBody); /** * 获取处理器名字 * * @return 处理器名字 */ String getName(); }

RequestBody 对请求的参数进行封装

/** * 请求处理封装类 * * @author tianp **/ public class RequestBody { private String uri; //根据业务定义 private Object body; public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public Object getBody() { return body; } public void setBody(Object body) { this.body = body; } }

处理器接口 “Hanlder” 定义了三个标准:

是否支持处理。通过参数 uri 判断当前 “Handler” 是否支持处理, 如果当前不能处理就传递给下一个 “Handler” 处理真正进行处理的方法。传递 RequestBody给 handle 方法,处理成功则传递下一个,不成功则返回false,结束。获取当前处理器名字。便于打印日志排查问题 /** * @author tianp **/ public abstract class AbstractHandler implements Handler { /** * 处理器名称 */ private String name; /** * 拦截uri */ private String[] includePatterns; /** * 放行uri */ private String[] excludePatterns; /** * ant 匹配 */ private PathMatcher pathMatcher = new AntPathMatcher(); /** * 下一个处理器 */ private AbstractHandler next = null; public AbstractHandler(String name) { this.name = name; } public boolean support(String uri) { if (excludePatterns != null) { for (String exclude : excludePatterns) { if (pathMatcher.match(exclude, uri)) { return false; } } } if (includePatterns != null) { for (String include : includePatterns) { if (pathMatcher.match(include, uri)) { return true; } } } return false; } /** * 给子类实现的真正处理方法 * * @param requestBody 请求参数 * @return true 成功 false 失败 */ public abstract boolean process(RequestBody requestBody); public boolean handle(RequestBody requestBody) { if (next != null) { if (next.support(requestBody.getUri())) { System.out.println(next.getName() + "开始处理"); return next.process(requestBody); } else { next.handle(requestBody); } } return true; } } //.... 省略 get/set 方法

抽象处理器 AbstractHandler 实现了 Handler 接口,实现了对应的

support() 通过 AntPathMatcher 使用 ant 风格的表达式来匹配当前类是否支持拦截handle() 通过持有一个 next 指针来进行请求传递getName()process() 给子类实现的真正处理的方法 /** * @author tianp **/ public class LinkedHandlerChain { /** * 头节点 */ private static final AbstractHandler HEAD = new AbstractHandler("head"){ @Override public boolean process(RequestBody requestBody) { return true; } }; private AbstractHandler TAIL = HEAD; public void addLast(AbstractHandler handler){ TAIL.setNext(handler); TAIL = handler; } public boolean handle(RequestBody requestBody){ return HEAD.handle(requestBody); } }

最后通过一个链 LinkedhandlerChain 串起来。通过持有链表的指针 HEAD、TAIL 来处理和添加节点。

优点

非常显著的优点就是将请求和处理分开。请求者不用知道是谁处理的,处理者不用知道请求的全貌。两者解耦,提高系统灵活性

缺点

性能问题。每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。调试不方便。当链比较长、环节比较多的时候,由于采取了类似递归的方式,调试的时候逻辑可能比较复杂

最佳实践

责任链在实际项目中,曾经在项目中使用它来对一部分功能进行鉴权,因为当时只有一部分接口需要鉴权,如果引入第三方框架如:Spring Security 和 Shiro 不仅增加项目的复杂度,还让项目变 “重”,因此通过引入 责任链模式,我可以很好的解决这个问题

更多参考

代码仓库地址:https://github.com/To-echo/chain-design/tree/master

参考书籍:《设计模式之禅》

转载于:https://www.cnblogs.com/coding400/p/11596309.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)