前言
最近工作中有个业务场景非常适合应用责任链模式,且有好几个中央都能应用到。为了写一个比拟通用且欠缺的责任链,浏览了 Spring 框架中一些责任链的实现作为参考。
Spring 中责任链模式的利用
责任链的利用十分宽泛,在 Spring 中都有很多利用,这里剖析两个十分罕用的性能是如何实现的。
Spring Web 中的 HandlerInterceptor
HandlerInterceptor
接口在web开发中十分罕用,外面有preHandle()
、postHandle()
、afterCompletion()
三个办法,实现这三个办法能够别离在调用"Controller"办法之前,调用"Controller"办法之后渲染"ModelAndView"之前,以及渲染"ModelAndView"之后执行。
public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { }}
HandlerInterceptor
在责任链中充当解决者的角色,通过HandlerExecutionChain
进行责任链调用。
public class HandlerExecutionChain { ... @Nullable private HandlerInterceptor[] interceptors; private int interceptorIndex = -1; boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = 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); } } } void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { HandlerInterceptor[] interceptors = 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 ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } }}
调用的形式非常简单,即通过数组存储注册在Spring中的HandlerInterceptor
,而后通过interceptorIndex
作为指针去遍历责任链数组按顺序调用解决者。
Spring AOP
Spring AOP实现的更加灵便,能够实现前置(@Before
)、后置(@After
)、盘绕(@Around
)等多种切入机会。目前Spring AOP 的动静代理有两种实现:JdkDynamicAopProxy
和CglibAopProxy
。这里就以CglibAopProxy
的实现流程看一下责任链的应用。
首先Spring会依据配置等决定对一个对象进行代理,JdkDynamicAopProxy
实现了InvocationHandler
接口并实现invoke()
办法。相熟JDK动静代理的都晓得通过代理对象调用办法时,会进入到InvocationHandler
对象的invoke()
办法,所以咱们间接从JdkDynamicAopProxy
的这个办法开始:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ... try { // 1. 校验及跳过一些无需代理的办法 ... // 2. 获取指标办法所有的拦截器 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { // 没有拦截器则间接反射调用指标办法 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // 3 生成动静代理的责任链 MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 4 执行责任链 retVal = invocation.proceed(); } // 5. 组装解决返回值 ... return retVal; } ... }}
这个办法整体流程比拟长的,我只展现了和责任链无关的局部。步骤2通过获取和指标代理办法相干的所有Advice
,Advice
能够说是拦截器在Spring中的封装,即咱们编写的AOP办法的封装。接着如果有拦截器则通过步骤3生成一个ReflectiveMethodInvocation
,并且执行该责任链。
再看看ReflectiveMethodInvocation
的proceed()
办法是怎么样的:
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable { protected ReflectiveMethodInvocation( Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments, @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) { this.proxy = proxy; this.target = target; this.targetClass = targetClass; this.method = BridgeMethodResolver.findBridgedMethod(method); this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments); this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; } public Object proceed() throws Throwable { if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { // 1. 如果以后Index和interceptorsAndDynamicMethodMatchers的拦截器数量雷同,阐明责任链调用完结,间接反射调用指标代理的办法 return invokeJoinpoint(); } // 2. 获取以后的拦截器 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // InterceptorAndDynamicMethodMatcher 类型的拦截器,校验MethodMatcher是否匹配 InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); // 3. 指标代理办法和以后拦截器是否matches if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { // 4. 执行以后拦截器 return dm.interceptor.invoke(this); } else { // 5. 不匹配则递归调用下一个拦截器 return proceed(); } } else { // 执行以后拦截器 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }}
- 首先
ReflectiveMethodInvocation
在构造方法中保留JdkDynamicAopProxy
传入的拦截器列表(chain
)到interceptorsAndDynamicMethodMatchers
。
- 接着在
proceed()
办法中,先断定以后currentInterceptorIndex
指针是否到拦截器列表size,如果到了阐明拦截器都执行过了,就去调用指标代理办法。 - 否则获取以后拦截器,通常类型为
InterceptorAndDynamicMethodMatcher
,InterceptorAndDynamicMethodMatcher
外面只有两个属性,MethodMatcher
和MethodInterceptor
,前者用于断定拦截器是否须要匹配指标代理办法,后者就是拦截器自身。 - 如果以后
InterceptorAndDynamicMethodMatcher
匹配指标代理办法,则调用以后拦截器,否则间接再次调用以后proceed()
造成递归。
ReflectiveMethodInvocation
就是责任链中的“链条”,解决者是InterceptorAndDynamicMethodMatcher
,更精确的说是InterceptorAndDynamicMethodMatcher
里的MethodInterceptor
实现类。这里的MethodInterceptor
在Spring中被封装成各种Advice
,例如盘绕(@Around
)的切面会被封装成AspectJAroundAdvice
。
AspectJAroundAdvice
的具体代码先不看,先写一个最根本的AOP切面
@Slf4j@Aspect@Componentpublic class LogAop { @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)") public void logPointCut() { } @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { log.info("LogAop start"); Object result = point.proceed(); log.info("LogAop end"); return result; }}
@Pointcut
里的匹配规定会被封装到InterceptorAndDynamicMethodMatcher
的MethodMatcher
作为匹配规定,而@Around
注解的办法就会被封装到MethodInterceptor
,这里即是AspectJAroundAdvice
。在AspectJAroundAdvice
里会封装ProceedingJoinPoint
等参数,而后作为入参反射调用对应的AOP办法。
public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, Serializable { protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { // 1. args AOP办法的入参,通常为ProceedingJoinPoint Object[] actualArgs = args; if (this.aspectJAdviceMethod.getParameterCount() == 0) { actualArgs = null; } try { ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); // 2. this.aspectInstanceFactory.getAspectInstance()获取AOP切面的实例 return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); } catch (IllegalArgumentException ex) { throw new AopInvocationException("Mismatch on arguments to advice method [" + this.aspectJAdviceMethod + "]; pointcut expression [" + this.pointcut.getPointcutExpression() + "]", ex); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }}
入参ProceedingJoinPoint
,外面蕴含了指标代理办法的一些信息,更重要的是咱们会调用该对象的proceed()
办法来尝试调用指标代理办法。即咱们能够在AOP切面调用proceed()
办法前后编写咱们冀望在指标代理办法前后要执行的代码。
如在LogAop
里会在指标代理办法执行前打印LogAop start
,执行后打印LogAop end
。
ProceedingJoinPoint.proceed()
又是怎么做的呢,这里看下Spring的实现类MethodInvocationProceedingJoinPoint
的代码。
public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart { @Override public Object proceed() throws Throwable { return this.methodInvocation.invocableClone().proceed(); }}
很简略的一段代码,克隆methodInvocation
并调用其proceed()
办法。这里的methodInvocation
就是一开始提到的ReflectiveMethodInvocation
,所以绕了一大圈,实际上造成了ReflectiveMethodInvocation.proceed()
的递归。
![]()
两种实现总结
Spring Web 中的 HandlerInterceptor
采纳数组的程序遍历模式来管制责任链链条的推动,这种模式能够让解决者无需手动管制链条,每个解决者之间也不会互相收到烦扰。然而同时解决者就无奈打断责任链,必须解决完所有的解决者才会完结。且解决者的调用机会也是由链来决定,绝对没那么灵便。
Spring AOP则采纳递归的形式,解决者要在本人的性能里显示的调用来推动链条,这样绝对比拟灵便,能够本人决定推动机会,甚至能够打断责任链。这样的话对其余解决者来说有点不可控,有时责任链被其余解决者打断而导致本人未被调用到,减少了调试的艰难。
这两种责任链的链条都应用Index
变量来管制链条推动,这就意味着无奈“共享”一个链条,每次应用责任链都要从新生成,用完又须要销毁,比拟耗费处理器资源。
设计通用责任链
在后面学习了其余责任链的实现,当初本人实现一个通用的的责任链。
先写一个根底的处理器接口:
/** * 通用责任链的 Handler * <p> * 每个业务申明一个该接口的子接口或者抽象类,再基于该接口实现对应的业务 Handler。 * 这样 BaseHandlerChain 能够间接注入到对应的 Handler List */public interface BaseHandler<Param, Result> { @NonNull HandleResult<Result> doHandle(Param param); default boolean isHandler(Param param) { return true; }}
处理器接口有两个办法,isHandler()
用于断定该处理器是否须要执行,默认为true
。doHandle()
办法执行处理器逻辑,且返回HandleResult
用于返回处理结果,并断定是否继续执行下一个处理器。
@Getterpublic class HandleResult<R> { private final R data; private final boolean next; private HandleResult(R r, boolean next) { this.data = r; this.next = next; } public static <R> HandleResult<R> doNextResult() { return new HandleResult<>(null, true); } public static <R> HandleResult<R> doCurrentResult(R r) { return new HandleResult<>(r, false); }}
最初编写责任链控制器的代码:
/** * 通用责任链模式 * <p> * 应用办法: * <p> * 1. 创立一个对应业务的责任链控制器, 继承 BaseHandlerChain, * 如: {@code MyHandlerChain extends BaseHandlerChain<MyHandler, MyParam, MyResult>} * <p> * 2. 创立一个对应业务的责任链处理器 Handler,继承 BaseHandler, * 如: {@code MyHandler extends BaseHandler<MyParam, MyResult>} * <p> * 3. 编写业务须要的处理器 Handler 实现 MyHandler 接口的 doHandle 办法。举荐把控制器和处理器都交给 Spring 管制,能够间接注入。 */public class BaseHandlerChain<Handler extends BaseHandler<Param, Result>, Param, Result> { @Getter private final List<Handler> handlerList; public BaseHandlerChain(List<Handler> handlerList) { this.handlerList = handlerList; } public Result handleChain(Param param) { for (Handler handler : handlerList) { if (!handler.isHandler(param)) { continue; } HandleResult<Result> result = handler.doHandle(param); if (result.isNext()) { continue; } return result.getData(); } return null; }}
这样就责任链就实现了,当初来做个简略的应用演示
/** * 基于业务的根底处理器接口 */public interface MyHandler extends BaseHandler<String, String> {}/** * 基于业务的控制器接口 */@Servicepublic class MyHandlerChain extends BaseHandlerChain<MyHandler, String, String> { @Autowired public MyHandlerChain(List<MyHandler> myHandlers) { super(myHandlers); }}/** * 处理器1 */@Slf4j@Order(Ordered.HIGHEST_PRECEDENCE)@Componentpublic class MyLogHandler implements MyHandler { @Override public @NonNull HandleResult<String> doHandle(String param) { log.info("MyLogHandler hello {} !", param); return HandleResult.doNextResult(); }}/** * 处理器2 */@Slf4j@Componentpublic class MyDefaultHandler implements MyHandler { @Override public @NonNull HandleResult<String> doHandle(String param) { log.info("param is {}", param); return HandleResult.doCurrentResult("MyDefaultHandler"); }}/** * 单元测试 */@Slf4j@SpringBootTestpublic class BaseHandlerChainTests { @Autowired private MyHandlerChain handlerChain; @Test public void handleChain() { String result = handlerChain.handleChain("zzzzbw"); log.info("handleChain result: {}", result); }}
最初日志会输入如下:
INFO 6716 --- [ main] c.z.s.demo.chain.handler.MyLogHandler : MyLogHandler hello zzzzbw !INFO 6716 --- [ main] c.z.s.d.chain.handler.MyDefaultHandler : param is zzzzbwINFO 6716 --- [ main] c.z.s.demo.BaseHandlerChainTests : handleChain result: MyDefaultHandler
参考
责任链模式实现的三种形式
责任链模式的两种实现
责任链的三种实现形式比拟
责任链的2种实现形式,你更pick哪一种
责任链模式(Chain of Responsibility Pattern)。
这一次搞懂Spring代理创立及AOP链式调用过程 - 云+社区 - 腾讯云 (tencent.com)
原文地址: 责任链模式在 Spring 中的利用