将一堆“作业”串联在一起,有序履行,就叫职责链
一、概述
职责链形式(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