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

原创 剑秋 人人架构师 2022-05-23 09:04 发表于四川

收录于合集

#技术系列51个

#设计模式2个

 

1. 什么是责任链模式

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

如图,每个节点只负责自己处理的事情,节点1处理完了再到节点2,节点2处理完了再到节点3

优点:

降低了对象之间的耦合度,该模式使得一个对象无须知道到底是哪一个对象处理其请求。

责任链简化了对象之间的连接,每个对象只需要保持一个后继者的引用,不需要保持其他所有的处理者,避免了使用众多的if/else责任分担,每个类只处理自己该处理的工作,不该处理的传递给下一个对象完成。

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

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

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

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

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

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

3. 责任链模式项目实战

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

 

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

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

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

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

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

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

public class OrderService {
    public void placeOrder() {
        checkActive();
        checkPurchaseMaxCount();
        checkCanPurchaseByUser();
        checkCanUseCoupon();

        System.out.println("购买成功");
    }

    private void checkActive() {
        System.out.println("检查此商品是否还处于活动中....");
        System.out.println("检查通过===========");
    }

    private void checkPurchaseMaxCount() {
        System.out.println("检查此商品能购买的最大数量....");
        System.out.println("检查通过===========");
    }

    private void checkCanPurchaseByUser() {
        System.out.println("检查此购买用户是否有资格购买此商品....");
        throw new RuntimeException("检查失败,此用户不具备购买资格");
    }

    private void checkCanUseCoupon() {
        System.out.println("检查此商品是否能使用优惠券....");
    }

    public static void main(String[] args) {
       OrderService orderService =  new OrderService();
       orderService.placeOrder();
    }
}

5. 传统责任链模式写法

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

public abstract class IHandler {
    // 下一个处理器
    protected IHandler handler;

    abstract void handle();

    public void setHandler(IHandler handler) {
        this.handler = handler;
    }
}
public class CheckActiveHandler extends IHandler{
    @Override
    public void handle() {
        System.out.println("检查此商品是否还处于活动中....");
        System.out.println("检查通过===========");
        // 进入下一个处理器
        super.handler.handle();
    }
}

public class CheckPurchaseMaxCountHandler extends IHandler{
    @Override
    public void handle() {
        System.out.println("检查此商品能购买的最大数量....");
        System.out.println("检查通过===========");
        super.handler.handle();
    }
}

public class CheckCanQualifiedToPurchaseHandler extends IHandler{
    @Override
    public void handle() {
        System.out.println("检查此购买用户是否有资格购买此商品....");
        throw new RuntimeException("检查失败,此用户不具备购买资格");
    }
}
public class CheckCanUseCouponHandler extends IHandler{
    @Override
    public void handle() {
        System.out.println("检查此商品是否能使用优惠券....");
    }
}

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

public class OrderService2 {
    public void placeOrder() {
        IHandler checkActiveHandler = new CheckActiveHandler();
        IHandler checkCanQualifiedToPurchaseHandler = new CheckCanQualifiedToPurchaseHandler();
        IHandler checkCanUseCouponHandler = new CheckCanUseCouponHandler();
        IHandler checkPurchaseMaxCountHandler = new CheckPurchaseMaxCountHandler();

        // 按顺序设置下个处理器
        checkActiveHandler.setHandler(checkPurchaseMaxCountHandler);
        checkPurchaseMaxCountHandler.setHandler(checkCanQualifiedToPurchaseHandler);
        checkCanQualifiedToPurchaseHandler.setHandler(checkCanUseCouponHandler);

        checkActiveHandler.handle();
        System.out.println("购买成功");
    }

    public static void main(String[] args) {
        OrderService2 orderService = new OrderService2();
        orderService.placeOrder();
    }
}

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

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

public interface IHandler {
    void check();
}
@Component
@Order(1) //设置检查的顺序,这是第一个检查
public class CheckActiveHandler implements IHandler {

    @Override
    public void check() {
        System.out.println("检查此商品是否还处于活动中....");
        System.out.println("检查通过===========");
    }
}

@Component
@Order(2)
public class CheckPurchaseMaxCountHandler implements IHandler {
    @Override
    public void check() {
        System.out.println("检查此商品能购买的最大数量....");
        System.out.println("检查通过===========");
    }
}

@Component
@Order(3)
public class CheckCanQualifiedToPurchaseHandler implements IHandler {

    @Override
    public void check() {
        System.out.println("检查此购买用户是否有资格购买此商品....");
        throw new RuntimeException("检查失败,此用户不具备购买资格");
    }
}

@Component
@Order(4)
public class CheckCanUseCouponHandler implements IHandler {
    @Override
    public void check() {
        System.out.println("检查此商品是否能使用优惠券....");
    }
}


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

@Service
public class OrderService3 {
    @Autowired //spring会按order顺序,自动注入IHandler的所有实现类
    private List<IHandler> handlers;

    public void placeOrder() {
        handlers.forEach(handler -> handler.check());
        System.out.println("购买成功");
    }


}

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

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

Spring的责任链模式运用

Spring Web 中的 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作为指针去遍历责任链数组按顺序调用处理者。

mybatis使用责任链模式

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

public class Plugin implements InvocationHandler{
    private Object target;
    private Interceptor interceptor;
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {      
        if (满足代理条件) {
            return interceptor.intercept(new Invocation(target, method, args));
        }
        return method.invoke(target, args);     
    }

    //对传入的对象进行代理,可能是实际的Executor类,也可能是Plugin代理类
    public static Object wrap(Object target, Interceptor interceptor) {

        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
            return Proxy.newProxyInstance(
                    type.getClassLoader(),
                    interfaces,
                    new Plugin(target, interceptor, signatureMap));
        }
        return target;
    }
} 

 简单的示意图如下:

8. 责任链模式总结

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

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

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

责任链模式 适用场景 : 

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


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

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

责任链模式 优点 :

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

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

责任链模式 缺点 :

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

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

9. 精彩分享

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
责任链模式是一种设计模式,它将请求的发送者和接收者解耦,并且允许多个对象都有机会处理这个请求。在整合Spring框架中,我们可以使用责任链模式来设计和实现安全认证方案,以确保系统的安全性。 Spring Security是一个功能强大的安全框架,它提供了多种内置的过滤器(Filter)来实现不同层次的安全控制。通过配置这些过滤器的顺序和规则,我们可以构建一个安全认证的责任。我们可以根据具体的需求和业务逻辑,自定义过滤器并将其添加到Spring Security的过滤器中,从而实现对请求进行安全认证的目的。 在整合Spring框架时,我们可以通过在控制器(Controller)的请求处理方法中添加相应的注解(@RequestMapping、@GetMapping等)来定义接收请求的路径和方法。同时,我们可以使用Spring Security的注解和配置来控制对这些请求的安全访问权限。例如,可以使用@PreAuthorize注解来定义只有满足特定条件的用户才能访问某个请求路径或方法。 此外,Spring Security提供了一些内置的过滤器,如表单登录过滤器、基于角色的访问控制过滤器、跨站点请求伪造(CSRF)过滤器等。这些过滤器可以根据配置的顺序和规则来对请求进行处理,实现不同层次的安全控制。 综上所述,我们可以借助责任链模式Spring Security框架来实现系统的安全认证。通过配置和定制过滤器,我们可以对请求进行安全访问控制,并保证系统的安全性。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值