image.png

1. 什么是责任链模式

定义:为了避免请求发送者和多个请求处理者之间的耦合,于是将所有请求的处理者通过前一对象记住其后一对象的引用。当有请求来时,可将请求沿着链传递,直到有处理者处理它为止。

如图,每个节点只负责自己处理的事情,节点1处理完了再到节点2,节点2处理完了再到节点3
责任链模式结合Spring优雅实现多层验证 - 图2

优点:

降低了对象之间的耦合度,该模式使得一个对象无须知道到底是哪一个对象处理其请求。
责任链简化了对象之间的连接,每个对象只需要保持一个后继者的引用,不需要保持其他所有的处理者,避免了使用众多的if/else责任分担,每个类只处理自己该处理的工作,不该处理的传递给下一个对象完成。

如果你仍然无法理解可以继续往下看实战应用

2. 责任链模式的2种形式

关于责任链模式,其有两种形式:

⼀种是通过外部调⽤的⽅式对链的各个节点调⽤进⾏控制,从⽽进⾏链的各个节点之间的切换;

另⼀种是链的每个节点⾃由控制是否继续往下传递链的进度,这种⽐较典型的使⽤⽅式就是Netty中的责任链模式。

本文对这2种形式都会有对应的代码并且会说明是哪种形式。

3. 责任链模式项目实战

本文以电商系统中下单操作为例子:假设某商品处于活动中,下单会经过以下校验:

责任链模式结合Spring优雅实现多层验证 - 图3

  1. 商品是否还处于活动中。如果没在活动中,则不能下单了。

  2. 此商品购买的最大数量。如果下单的数量超出了最大允许购买的数量,下单失败。

  3. 哪些人能购买。此商品只能允许部分人购买,其他人不具备购买资格如果下单,将会下单失败。

  4. 此商品是否能用劵购买。此商品不能使用优惠券,使用优惠券则会下单失败。

    4. 没用责任链模式的写法

    我们先看看传统如何写这个校验代码,每个校验操作写一个方法,本文将会以最简单的代码去演示。

    1. public class OrderService {
    2. public void placeOrder() {
    3. checkActive();
    4. checkPurchaseMaxCount();
    5. checkCanPurchaseByUser();
    6. checkCanUseCoupon();
    7. System.out.println("购买成功");
    8. }
    9. private void checkActive() {
    10. System.out.println("检查此商品是否还处于活动中....");
    11. System.out.println("检查通过===========");
    12. }
    13. private void checkPurchaseMaxCount() {
    14. System.out.println("检查此商品能购买的最大数量....");
    15. System.out.println("检查通过===========");
    16. }
    17. private void checkCanPurchaseByUser() {
    18. System.out.println("检查此购买用户是否有资格购买此商品....");
    19. throw new RuntimeException("检查失败,此用户不具备购买资格");
    20. }
    21. private void checkCanUseCoupon() {
    22. System.out.println("检查此商品是否能使用优惠券....");
    23. }
    24. public static void main(String[] args) {
    25. OrderService orderService = new OrderService();
    26. orderService.placeOrder();
    27. }
    28. }

5. 传统责任链模式写法

以下代码就是责任链模式中的节点控制模式,可以看到我在第一个节点里面我可以控制是否走下一个节点,对应的就是这句代码:super.handler.handle();

  1. public abstract class IHandler {
  2. // 下一个处理器
  3. protected IHandler handler;
  4. abstract void handle();
  5. public void setHandler(IHandler handler) {
  6. this.handler = handler;
  7. }
  8. }
  1. public class CheckActiveHandler extends IHandler{
  2. @Override
  3. public void handle() {
  4. System.out.println("检查此商品是否还处于活动中....");
  5. System.out.println("检查通过===========");
  6. // 进入下一个处理器
  7. super.handler.handle();
  8. }
  9. }
  1. public class CheckPurchaseMaxCountHandler extends IHandler{
  2. @Override
  3. public void handle() {
  4. System.out.println("检查此商品能购买的最大数量....");
  5. System.out.println("检查通过===========");
  6. super.handler.handle();
  7. }
  8. }
  1. public class CheckCanQualifiedToPurchaseHandler extends IHandler{
  2. @Override
  3. public void handle() {
  4. System.out.println("检查此购买用户是否有资格购买此商品....");
  5. throw new RuntimeException("检查失败,此用户不具备购买资格");
  6. }
  7. }
  1. public class CheckCanUseCouponHandler extends IHandler{
  2. @Override
  3. public void handle() {
  4. System.out.println("检查此商品是否能使用优惠券....");
  5. }
  6. }

下单操作首先要实例化所有的handler,然后设置好顺序

  1. public class OrderService2 {
  2. public void placeOrder() {
  3. IHandler checkActiveHandler = new CheckActiveHandler();
  4. IHandler checkCanQualifiedToPurchaseHandler = new CheckCanQualifiedToPurchaseHandler();
  5. IHandler checkCanUseCouponHandler = new CheckCanUseCouponHandler();
  6. IHandler checkPurchaseMaxCountHandler = new CheckPurchaseMaxCountHandler();
  7. // 按顺序设置下个处理器
  8. checkActiveHandler.setHandler(checkPurchaseMaxCountHandler);
  9. checkPurchaseMaxCountHandler.setHandler(checkCanQualifiedToPurchaseHandler);
  10. checkCanQualifiedToPurchaseHandler.setHandler(checkCanUseCouponHandler);
  11. checkActiveHandler.handle();
  12. System.out.println("购买成功");
  13. }
  14. public static void main(String[] args) {
  15. OrderService2 orderService = new OrderService2();
  16. orderService.placeOrder();
  17. }
  18. }

6. 责任链模式结合spring优雅实现

以下代码就是责任链模式中的外部控制模式,可以看到节点里面控制不了是否走下个节点了

  1. public interface IHandler {
  2. void check();
  3. }
  1. @Component
  2. @Order(1) //设置检查的顺序,这是第一个检查
  3. public class CheckActiveHandler implements IHandler {
  4. @Override
  5. public void check() {
  6. System.out.println("检查此商品是否还处于活动中....");
  7. System.out.println("检查通过===========");
  8. }
  9. }
  1. @Component
  2. @Order(2)
  3. public class CheckPurchaseMaxCountHandler implements IHandler {
  4. @Override
  5. public void check() {
  6. System.out.println("检查此商品能购买的最大数量....");
  7. System.out.println("检查通过===========");
  8. }
  9. }
  1. @Component
  2. @Order(3)
  3. public class CheckCanQualifiedToPurchaseHandler implements IHandler {
  4. @Override
  5. public void check() {
  6. System.out.println("检查此购买用户是否有资格购买此商品....");
  7. throw new RuntimeException("检查失败,此用户不具备购买资格");
  8. }
  9. }
  1. @Component
  2. @Order(4)
  3. public class CheckCanUseCouponHandler implements IHandler {
  4. @Override
  5. public void check() {
  6. System.out.println("检查此商品是否能使用优惠券....");
  7. }
  8. }


spring会按order注解标识的顺序,自动注入IHandler的所有实现类,所以我们直接循环调用就可以了

  1. @Service
  2. public class OrderService3 {
  3. @Autowired //spring会按order顺序,自动注入IHandler的所有实现类
  4. private List<IHandler> handlers;
  5. public void placeOrder() {
  6. handlers.forEach(handler -> handler.check());
  7. System.out.println("购买成功");
  8. }
  9. }

7. 开源框架中责任链模式的运用

开源框架中很多精彩的地方都运用了责任链模式,让我们一起看看吧!
Spring的责任链模式运用

Spring Web 中的 HandlerInterceptor接口在web开发中非常常用,里面有preHandle()、postHandle()、afterCompletion()三个方法,实现这三个方法可以分别在调用”Controller”方法之前,调用”Controller”方法之后渲染”ModelAndView”之前,以及渲染”ModelAndView”之后执行。

  1. public interface HandlerInterceptor {
  2. default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  3. throws Exception {
  4. return true;
  5. }
  6. default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  7. @Nullable ModelAndView modelAndView) throws Exception {
  8. }
  9. default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  10. @Nullable Exception ex) throws Exception {
  11. }
  12. }

HandlerInterceptor在责任链中充当处理者的角色,通过HandlerExecutionChain进行责任链调用。

  1. public class HandlerExecutionChain {
  2. ...
  3. @Nullable
  4. private HandlerInterceptor[] interceptors;
  5. private int interceptorIndex = -1;
  6. boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
  7. HandlerInterceptor[] interceptors = getInterceptors();
  8. if (!ObjectUtils.isEmpty(interceptors)) {
  9. for (int i = 0; i < interceptors.length; i++) {
  10. HandlerInterceptor interceptor = interceptors[i];
  11. if (!interceptor.preHandle(request, response, this.handler)) {
  12. triggerAfterCompletion(request, response, null);
  13. return false;
  14. }
  15. this.interceptorIndex = i;
  16. }
  17. }
  18. return true;
  19. }
  20. void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
  21. throws Exception {
  22. HandlerInterceptor[] interceptors = getInterceptors();
  23. if (!ObjectUtils.isEmpty(interceptors)) {
  24. for (int i = interceptors.length - 1; i >= 0; i--) {
  25. HandlerInterceptor interceptor = interceptors[i];
  26. interceptor.postHandle(request, response, this.handler, mv);
  27. }
  28. }
  29. }
  30. void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
  31. throws Exception {
  32. HandlerInterceptor[] interceptors = getInterceptors();
  33. if (!ObjectUtils.isEmpty(interceptors)) {
  34. for (int i = this.interceptorIndex; i >= 0; i--) {
  35. HandlerInterceptor interceptor = interceptors[i];
  36. try {
  37. interceptor.afterCompletion(request, response, this.handler, ex);
  38. }
  39. catch (Throwable ex2) {
  40. logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
  41. }
  42. }
  43. }
  44. }
  45. }

调用的方式非常简单,即通过数组存储注册在Spring中的HandlerInterceptor,然后通过interceptorIndex作为指针去遍历责任链数组按顺序调用处理者。
责任链模式结合Spring优雅实现多层验证 - 图4

mybatis使用责任链模式

Mybatis可以配置各种Plugin,无论是官方提供的还是自己定义的,Plugin和Filter类似,就在执行Sql语句的时候做一些操作。Mybatis的责任链则是通过动态代理的方式,使用Plugin代理实际的Executor类。(这里实际还使用了组合模式,因为Plugin可以嵌套代理),核心代码如下:

  1. public class Plugin implements InvocationHandler{
  2. private Object target;
  3. private Interceptor interceptor;
  4. @Override
  5. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  6. if (满足代理条件) {
  7. return interceptor.intercept(new Invocation(target, method, args));
  8. }
  9. return method.invoke(target, args);
  10. }
  1. //对传入的对象进行代理,可能是实际的Executor类,也可能是Plugin代理类
  2. public static Object wrap(Object target, Interceptor interceptor) {
  3. Class<?> type = target.getClass();
  4. Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
  5. if (interfaces.length > 0) {
  6. return Proxy.newProxyInstance(
  7. type.getClassLoader(),
  8. interfaces,
  9. new Plugin(target, interceptor, signatureMap));
  10. }
  11. return target;
  12. }
  13. }

简单的示意图如下:
责任链模式结合Spring优雅实现多层验证 - 图5

8. 责任链模式总结

责任链模式 , 又称为 职责链模式 ;

责任链模式定义 : 为 请求 创建一个接收该 请求对象 的 链 , 链条中每个元素都是一个对象 ;

责任链模式类型 : 行为型 ;

责任链模式 适用场景 :
**

① 多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定。

② 在请求处理者不明确的情况下向对个对象中的一个提交一个请求。

③ 需要动态处理一组对象处理请求。

责任链模式 优点 :

① 解耦 : 请求的 发送者 和 接收者 解耦 ; 接收者 是 请求的处理者 ;

② 动态组合 : 责任链 可以 动态组合 , 使用配置 设置责任链的 顺序及 是否出现 ; 可以随时对责任链排序 , 随时增加拆除责任链中的某个请求对象 ;

责任链模式 缺点 :

① 性能 : 如果 责任链 太长 , 或责任链中请求的 处理时间过长 , 可能会 影响性能 ;

② 个数 : 责任链 可能过多 ;