遇到无序多变请求怎么办,可以试试责任链模式

简介: 责任链模式(Chain Of Responsibility Design Pattern),也叫做职责链,是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止

责任链模式(Chain Of Responsibility Design Pattern),也叫做职责链,是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

一、责任链模式介绍

其实在日常生活中,有不少使用责任链的场景。比如公司采购审批流程,需要各个部门领导的批准同意。在责任链模式中,客户端只需要将请求发送到责任链上,无须关心请求的处理细节和传递,请求会自动进行传递。

1.1 责任链模式的结构

责任链模式结构的核心在于引入了一个抽象处理类,所以主要包含两个部分

  • 一个抽象Handler类
    • 因为责任链的存在,需要定义下一个handler的引用,才能形成一个链条
    • 有了下一个handler引用,所以要对引用进行调用和管理,于是需要setNext()getNext() 方法
    • 此外由于不同handler处理请求的方式不一致,所以要在其中定义一个抽象的请求方法handleRequest()
  • 一系列具体的Handler处理类

具体结构大致如下所示:

image-20220404203259612

  • abstract Handler:抽象处理者,定义一个处理请求的接口,内部包含抽象处理方法和后继具体处理者
  • Handler1、Handler2:具体处理者,具体实现抽象处理者的方法,并对请求做一些逻辑处理
  • Client:客户端,使用职责链模式

1.2 职责链模式的实现

根据上面的类图,可以实现如下代码:

/**
 * @description: 抽象处理类
 * @date: 2022/4/4
 */
public abstract class Handler {
   
   

    private Handler successor;

    public Handler getSuccessor() {
   
   
        return successor;
    }

    public void setSuccessor(Handler successor) {
   
   
        this.successor = successor;
    }

    /**
     * 处理请求的抽象方法
     * @param request 请求
     */
    public abstract void handleRequest(String request);
}
/**
 * @description: 具体处理者1
 * @date: 2022/4/4
 */
public class Handler1 extends Handler{
   
   

    private String handler;

    public Handler1(String handler) {
   
   
        this.handler = handler;
    }

    @Override
    public void handleRequest(String request) {
   
   
        if ("handler1".equals(request)) {
   
   
            System.out.println("具体处理者handler1进行请求处理");
        } else {
   
   
            if (getSuccessor() != null) {
   
   
                //如果指向下一个具体处理者
                getSuccessor().handleRequest(request);
            } else {
   
   
                System.out.println("没有处理者进行处理");
            }
        }
    }
}
/**
 * @description: 具体处理者2
 * @date: 2022/4/4
 */
public class Handler2 extends Handler{
   
   

    private String handler;

    public Handler2(String handler) {
   
   
        this.handler = handler;
    }

    @Override
    public void handleRequest(String request) {
   
   
        if ("handler2".equals(request)) {
   
   
            System.out.println("具体处理者handler2进行请求处理");
        } else {
   
   
            if (getSuccessor() != null) {
   
   
                getSuccessor().handleRequest(request);
            } else {
   
   
                System.out.println("请求没有被任何处理者处理");
            }
        }
    }
}
/**
 * @description: 客户端类
 * @date: 2022/4/4
 */
public class Client {
   
   
    public static void main(String[] args) {
   
   
        Handler handler1 = new Handler1("handler1");
        Handler handler2 = new Handler2("handler2");
        handler1.setSuccessor(handler2);
        handler1.handleRequest("handler1");
        handler1.handleRequest("handler2");

    }
}

测试结果:

具体处理者handler1进行请求处理
具体处理者handler2进行请求处理

二、责任链模式的应用场景

责任链适用的应用场景有:

  1. 当程序需要使用不同方式来处理不同种类的请求,而且各个请求类型和顺

职责链模式最常见的应用就是用来开发各种框架的过滤器和拦截器,比如Spring Interceptor和Servlet Filter

2.1 在Servlet Filter中的应用

Filter 可以实现对HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。比如一些Servlet 容器(TomCat、Jetty等)就支持Filter的过滤功能。以TomCat 为例:

image-20220404214532930

所以当Servlet 请求到来时,首先会经过Filter 处理,最后再到达Servlet实例。我这里选取的TomCat版本是SpringBoot自带的9.0,先来看看FilterChain 的接口:

public interface FilterChain {
   
   
    //Filter具体实现
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

ApplicationFilterChain是责任链模式的具体实现类:

public final class ApplicationFilterChain implements FilterChain {
   
   
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; //Filter配置数组
    private int pos = 0; //执行Filter的序号
    private int n = 0;     //目前Filter的个数
    private Servlet servlet = null;

    public ApplicationFilterChain() {
   
   
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
   
   
        //Filter 具体实现
        //...
    }

    public static ServletRequest getLastServicedRequest() {
   
   
        return (ServletRequest)lastServicedRequest.get();
    }

    public static ServletResponse getLastServicedResponse() {
   
   
        return (ServletResponse)lastServicedResponse.get();
    }

    void addFilter(ApplicationFilterConfig filterConfig) {
   
   
        ApplicationFilterConfig[] newFilters = this.filters;
        int var3 = newFilters.length;

        for(int var4 = 0; var4 < var3; ++var4) {
   
   
            ApplicationFilterConfig filter = newFilters[var4];
            if (filter == filterConfig) {
   
   
                return;
            }
        }
        //增加Filter
        if (this.n == this.filters.length) {
   
   
            newFilters = new ApplicationFilterConfig[this.n + 10];
            System.arraycopy(this.filters, 0, newFilters, 0, this.n);
            this.filters = newFilters;
        }

        this.filters[this.n++] = filterConfig;
    }
}

2.2 在Spring Interceptor中的应用

和Servlet Filter类似,在Spring 中也有对应的过滤器 Interceptor。它是由Spring MVC 框架来实现,借一张来自《设计模式之美》的图片来说明:

image-20220404221601450

客户端发送请求,首先会经过Servlet Filter,然后再经过Spring Interceptor,最后再到达具体的业务中。

和Filter一样,Interceptor 中也是基于责任链模式来实现的,与之相对的HandlerInterceptor是抽象处理接口:

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 {
   
   
    }
}

会有各种具体处理类的实现:

image-20220404222327991

此外在其他框架中也存在着职责链模式的使用,比如Dubbo Filter、Netty ChannelPipeline等等

三、责任链模式实战

3.1 模拟三级审批责任链

简单利用职责链来设计三级审批:

public static void main(String[] args) {
   
   
        //设计审批责任链
        Handler handler = new FirstHandler()
                .setNext(new SecondHandler()
                        .setNext(new ThirdHandler()));
        handler.doHandler("firstRequest");
    }
    //抽象处理类
    static abstract class Handler {
   
   
        private Handler next;

        public Handler getNext() {
   
   
            return next;
        }

        public Handler setNext(Handler next) {
   
   
            this.next = next;
            return this;
        }

        public abstract void doHandler(String request);
    }
    //第一级审批人
    static class FirstHandler extends Handler {
   
   

        @Override
        public void doHandler(String request) {
   
   
            System.out.println("第一个审批处理点审批中...");
            if (request == null || request == "") {
   
   
                System.out.println("第一个审批节点的请求为空!");;
            }
            System.out.println("第一个审批处理点审批完成");
            super.next.doHandler("secondRequest");
        }
    }
    //第二级审批人
    static class SecondHandler extends Handler {
   
   

        @Override
        public void doHandler(String request) {
   
   
            System.out.println("第二个审批处理点审批中...");
            if (request == null || request == ""){
   
   
                System.out.println("第二个审批节点的请求为空!");
            }
            System.out.println("第一个审批处理点审批完成");
            super.next.doHandler("thirdRequest");
        }
    }
    //第三级审批人
    static class ThirdHandler extends Handler {
   
   
        @Override
        public void doHandler(String request) {
   
   
            System.out.println("第三个审批处理点审批中...");
            Handler next = super.next;
            if (next == null){
   
   
                System.out.println("审批结束");
            }
        }
    }

3.2 模拟在618大促期间的业务系统上线审批流程场景:

像是这些⼀线电商类的互联⽹公司,阿⾥、京东、拼多多等,在618期间都会做⼀些运营活动场景以及 提供的扩容备战,就像过年期间百度的红包⼀样。但是所有开发的这些系统都需要陆续的上线,因为临 近618有时候也有⼀些紧急的调整的需要上线,但为了保障线上系统的稳定性是尽可能的减少上线的, 也会相应的增强审批⼒度。就像⼀级响应、⼆级响应⼀样。

⽽这审批的过程在随着特定时间点会增加不同级别的负责⼈加⼊,每个⼈就像责任链模式中的每⼀个核 ⼼点。对于研发并不需要关⼼具体的审批流程处理细节,只需要知道这个上线更严格,级别也更 ⾼,但对于研发⼈员来说同样是点击相同的提审按钮,等待审核。

使用责任链模式可以将各个服务模块按照一、二、三级进行分离,每个级别可以像Filter一样用Successor下一个级别的方法进行调用。具体结构图如下(来自《重学Java设计模式》)

image-20220404223642273

具体代码结构

│——AuthInfo.java
│——AuthLink.java
│
├─business
│     AuthService.java
│
└─impl
      Level1AuthLink.java
      Level2AuthLink.java
      Level3AuthLink.java

实现代码:

/**
 * @description: 链路抽象审批
 * @date: 2022/4/4
 */
public abstract class AuthLink {
   
   

    /**时间格式化**/
    protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /**级别人员ID**/
    protected String levelUserId;
    /**级别人员姓名**/
    protected String levelUserName;
    /**下一个链路**/
    private AuthLink next;

    public AuthLink(String levelUserId, String levelUserName) {
   
   
        this.levelUserId = levelUserId;
        this.levelUserName = levelUserName;
    }

    public AuthLink next() {
   
   
        return next;
    }

    public AuthLink appendNext(AuthLink next) {
   
   
        this.next = next;
        return this;
    }

    /**每个具体处理者必须实现的方法**/
    public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}
/**
 * @description: 责任链中返回对象的定义
 * @date: 2022/4/4
 */
public class AuthInfo {
   
   

    private String code;
    private String info = " ";

    public AuthInfo(String code, String... infos) {
   
    //...表示可变长参数,可以传入多个参数
        this.code = code;
        for (String s : infos) {
   
   
            this.info = this.info.concat(s);
        }
    }

    public String getCode() {
   
   
        return code;
    }

    public void setCode(String code) {
   
   
        this.code = code;
    }

    public String getInfo() {
   
   
        return info;
    }

    public void setInfo(String info) {
   
   
        this.info = info;
    }
}
/**
 * @description: 一级审核人
 * @date: 2022/4/4
 */
public class Level1AuthLink extends AuthLink {
   
   

    public Level1AuthLink(String levelUserId, String levelUserName) {
   
   
        super(levelUserId, levelUserName);
    }

    @Override
    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
   
   
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
   
   
            return new AuthInfo("0001", "单号", orderId, "状态:一级审批负责人", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
   
   
            return new AuthInfo("0000", "单号", orderId, "状态:一级审批负责人", " 时间:", f.format(date), "审批人:", levelUserName);
        }
        return next.doAuth(uId, orderId, authDate);
    }
}
/**
 * @description: 测试类
 * @date: 2022/4/5
 */
public class ApiTest {
   
   
    private Logger logger = LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void test_AuthLink() throws ParseException {
   
   
        //创建审批链条
       AuthLink authLink = new Level3AuthLink("1000013", "王工")
                .appendNext(new Level2AuthLink("1000012", "张经理")
                        .appendNext(new Level1AuthLink("1000011", "赵总裁")));

        logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("ethan", "100099800423123", new Date())));
        //将单号和用户id存入authMap中
        AuthService.auth("1000013", "100099800423123");
        logger.info("测试结果:{}", "模拟二级审批,王工");
        logger.info("测试结果:{}",JSON.toJSONString(authLink.doAuth("ethan", "100099800423123", new Date())));

        AuthService.auth("1000012", "100099800423123");
        logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
        logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("ethan", "100099800423123", new Date())));

        AuthService.auth("1000011", "100099800423123");
        logger.info("测试结果:{}", "模拟二级负责人审批,赵总裁");
        logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("ethan", "100099800423123", new Date())));

    }
}

最后的测试结果:

10:05:08.675 [main] INFO  ApiTest - 测试结果:{
   
   "code":"0001","info":" 单号:100099800423123 状态:待三级审批 王工"}
10:05:08.679 [main] INFO  ApiTest - 测试结果:模拟三级审批,王工
10:05:08.680 [main] INFO  ApiTest - 测试结果:{
   
   "code":"0001","info":" 单号:100099800423123 状态:待二级审批负责人张经理"}
10:05:08.680 [main] INFO  ApiTest - 测试结果:模拟二级负责人审批,张经理
10:05:08.680 [main] INFO  ApiTest - 测试结果:{
   
   "code":"0001","info":" 单号100099800423123状态:待一级审批负责人赵总裁"}
10:05:08.680 [main] INFO  ApiTest - 测试结果:模拟一级负责人审批,赵总裁
10:05:08.680 [main] INFO  ApiTest - 测试结果:{
   
   "code":"0000","info":" 单号100099800423123状态:一级审批完成负责人 时间:2023-09-11 10:05:08审批人:赵总裁"}

参考资料

《设计模式之美》

http://c.biancheng.net/view/4024.html

《重学Java设计模式》

目录
相关文章
|
6月前
|
设计模式 存储 算法
协议解析必用的责任链模式
协议解析必用的责任链模式
60 0
|
存储 监控 小程序
《优化接口设计的思路》系列:第三篇—留下用户调用接口的痕迹
大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。
97 0
《优化接口设计的思路》系列:第三篇—留下用户调用接口的痕迹
|
设计模式 缓存 算法
JAVA设计模式14:策略模式,使算法的变化独立于使用它的客户端
JAVA设计模式14:策略模式,使算法的变化独立于使用它的客户端
101 0
|
Java
Java后台返回树形结构的二种方式
Java后台返回树形结构的二种方式
442 0
|
设计模式 JavaScript 前端开发
|
6月前
|
设计模式
二十三种设计模式全面解析-当你的对象需要知道其他对象的状态变化时,观察者模式是你的救星!
二十三种设计模式全面解析-当你的对象需要知道其他对象的状态变化时,观察者模式是你的救星!
|
设计模式 数据安全/隐私保护
这才是责任链模式的优雅使用方式
首先创建一个实体类Member。
106 0
|
数据可视化
UpSet|多集合韦恩图 看不太清楚咋整?用upSet吧!
UpSet|多集合韦恩图 看不太清楚咋整?用upSet吧!
168 0
|
Java 测试技术 容器
工作中责任链模式用法及其使用场景?
工作中责任链模式用法及其使用场景?
128 0
|
消息中间件 存储 运维
难住了,微服务之间的几种调用方式哪种最佳?
难住了,微服务之间的几种调用方式哪种最佳?