乐趣区

关于后端:责任链模式在-Spring-中的应用

前言

最近工作中有个业务场景非常适合应用责任链模式,且有好几个中央都能应用到。为了写一个比拟通用且欠缺的责任链,浏览了 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 的动静代理有两种实现:JdkDynamicAopProxyCglibAopProxy。这里就以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 通过获取和指标代理办法相干的所有 AdviceAdvice 能够说是拦截器在 Spring 中的封装,即咱们编写的 AOP 办法的封装。接着如果有拦截器则通过步骤 3 生成一个ReflectiveMethodInvocation,并且执行该责任链。

再看看 ReflectiveMethodInvocationproceed()办法是怎么样的:

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);
        }
    }
}
  1. 首先 ReflectiveMethodInvocation 在构造方法中保留 JdkDynamicAopProxy 传入的拦截器列表 (chain) 到interceptorsAndDynamicMethodMatchers
  1. 接着在 proceed() 办法中,先断定以后 currentInterceptorIndex 指针是否到拦截器列表 size,如果到了阐明拦截器都执行过了,就去调用指标代理办法。
  2. 否则获取以后拦截器,通常类型为 InterceptorAndDynamicMethodMatcherInterceptorAndDynamicMethodMatcher 外面只有两个属性,MethodMatcherMethodInterceptor,前者用于断定拦截器是否须要匹配指标代理办法,后者就是拦截器自身。
  3. 如果以后 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里的匹配规定会被封装到 InterceptorAndDynamicMethodMatcherMethodMatcher作为匹配规定,而 @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()用于断定该处理器是否须要执行,默认为 truedoHandle() 办法执行处理器逻辑,且返回 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 中的利用

退出移动版