共计 10774 个字符,预计需要花费 27 分钟才能阅读完成。
前言
最近工作中有个业务场景非常适合应用责任链模式,且有好几个中央都能应用到。为了写一个比拟通用且欠缺的责任链,浏览了 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
@Component
public 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
用于返回处理结果,并断定是否继续执行下一个处理器。
@Getter
public 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> {
}
/**
* 基于业务的控制器接口
*/
@Service
public class MyHandlerChain extends BaseHandlerChain<MyHandler, String, String> {
@Autowired
public MyHandlerChain(List<MyHandler> myHandlers) {super(myHandlers);
}
}
/**
* 处理器 1
*/
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class MyLogHandler implements MyHandler {
@Override
public @NonNull HandleResult<String> doHandle(String param) {log.info("MyLogHandler hello {} !", param);
return HandleResult.doNextResult();}
}
/**
* 处理器 2
*/
@Slf4j
@Component
public class MyDefaultHandler implements MyHandler {
@Override
public @NonNull HandleResult<String> doHandle(String param) {log.info("param is {}", param);
return HandleResult.doCurrentResult("MyDefaultHandler");
}
}
/**
* 单元测试
*/
@Slf4j
@SpringBootTest
public 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 zzzzbw
INFO 6716 --- [main] c.z.s.demo.BaseHandlerChainTests : handleChain result: MyDefaultHandler
参考
责任链模式实现的三种形式
责任链模式的两种实现
责任链的三种实现形式比拟
责任链的 2 种实现形式,你更 pick 哪一种
责任链模式(Chain of Responsibility Pattern)。
这一次搞懂 Spring 代理创立及 AOP 链式调用过程 – 云 + 社区 – 腾讯云 (tencent.com)
原文地址: 责任链模式在 Spring 中的利用