共计 10077 个字符,预计需要花费 26 分钟才能阅读完成。
将一堆“事件”串联在一起,有序执行,就叫责任链
一、概述
责任链模式(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, 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 长处
- 将申请与处了解耦。
- 申请解决者(节点对象)只须要关注本人感兴趣的申请进行解决即可,对于不感兴趣的申请,转发给下一个节点。
- 具备链式传递解决申请性能,申请发送者无需通晓链路构造,只需期待申请处理结果。
- 链路构造灵便,能够通过扭转链路的构造动静的新增或删减责任。
-
易于扩大新的申请解决类(节点),合乎 开闭准则。
5.2 毛病
- 责任链太长或者解决工夫过长,会影响整体性能。
-
如果节点对象存在循环援用时,会造成死循环,导致系统解体。
六、参考源码
编程文档:https://gitee.com/cicadasmile/butte-java-note 利用仓库:https://gitee.com/cicadasmile/butte-flyer-parent