前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从源码角度理解Java设计模式——责任链模式

从源码角度理解Java设计模式——责任链模式

作者头像
我叫刘半仙
发布2019-03-12 17:14:21
1.5K0
发布2019-03-12 17:14:21
举报
文章被收录于专栏:我叫刘半仙

本文内容思维导图如下:

一、责任链模式介绍

责任链模式定义:为请求创建一个处理此请求对象的链。

适用场景(核心):只要把你的请求抛给第一个处理者,不用关心谁处理的,并且最终会返回你一个结果。

优点:请求者和处理者解耦,请求者不用知道谁处理的,处理者可以不用知道请求的全貌。

缺点:每个请求从链头遍历到链尾,影响性能。代码调试时候不方便。

类型:行为型。

类图:

源码中的典型应用:

  1. Netty 中的 Pipeline和ChannelHandler通过责任链设计模式来组织代码逻辑。
  2. Spring Security 使用责任链模式,可以动态地添加或删除责任(处理 request 请求)。ref:SPRING与设计模式---责任链模式
  3. Spring AOP 通过责任链模式来管理 Advisor。
  4. Dubbo Filter 过滤器链也是用了责任链模式(链表),可以对方法调用做一些过滤处理,譬如超时(TimeoutFilter),异常(ExceptionFilter),Token(TokenFilter)等。
  5. Mybatis 中的 Plugin 机制使用了责任链模式,配置各种官方或者自定义的 Plugin,与 Filter 类似,可以在执行 Sql 语句的时候做一些操作。
  6. Tomcat 调用 ApplicationFilterFactory过滤器链。

二、请假示例

员工在OA系统中提交请假申请,首先项目经理处理,他能审批3天以内的假期,如果大于3天,则由项目经理则转交给总经理处理。接下来我们用责任链模式实现这个过程。

1、封装请假信息实体类

代码语言:javascript
复制
public class LeaveRequest {
    private String name;    // 请假人姓名
    private int numOfDays;  // 请假天数
    private int workingAge;  //员工工龄(在公司大于2年则总经理会审批)
   //省略get..set..
}

2、抽象处理者类 Handler,维护一个nextHandler属性,该属性为当前处理者的下一个处理者的引用;声明了抽象方法process,其实在这里也用了方法模板模式:

代码语言:javascript
复制
public abstract class ApproveHandler {

    protected  ApproveHandler nextHandler;//下一个处理者(与类一致,这段代码很重要)

    public void setNextHandler(ApproveHandler approveHandler){
        this.nextHandler=approveHandler;
    }

    public abstract void process(LeaveRequest leaveRequest); // 处理请假(这里用了模板方法模式)

}

3、项目经理处理者,能处理小于3天的假期,而请假信息里没有名字时,审批不通过:

代码语言:javascript
复制
public class PMHandler extends ApproveHandler{

    @Override
    public void process(LeaveRequest leaveRequest) {
        //未填写姓名的请假单不通过
        if(null != leaveRequest.getName()){
            if(leaveRequest.getNumOfDays() <= 3){
                System.out.println(leaveRequest.getName()+",你通过项目经理审批!");
            }else {
                System.out.println("项目经理转交总经理");
                if(null != nextHandler){
                    nextHandler.process(leaveRequest);
                }
            }
        }else {
            System.out.println("请假单未填写完整,未通过项目经理审批!");
            return;
        }
    }
}

4、总经理处理者,能处理大于3天的假期,且工龄超过2年才会审批通过:

代码语言:javascript
复制
public class GMHandler extends ApproveHandler{

    @Override
    public void process(LeaveRequest leaveRequest) {
        //员工在公司工龄超过2年,则审批通过
        if(leaveRequest.getWorkingAge() >=2 && leaveRequest.getNumOfDays() > 3){
            System.out.println(leaveRequest.getName()+",你通过总经理审批!");
            if(null != nextHandler){
                nextHandler.process(leaveRequest);
            }
        }else {
            System.out.println("在公司年限不够,长假未通过总经理审批!");
            return;
        }
    }
}

实例代码完成,我们测试一下:

代码语言:javascript
复制
public class Test {
    public static void main(String[] args) {
        PMHandler pm = new PMHandler();
        GMHandler gm = new GMHandler();

        LeaveRequest leaveRequest = new LeaveRequest();
        leaveRequest.setName("张三");
        leaveRequest.setNumOfDays(4);//请假4天
        leaveRequest.setWorkingAge(3);//工龄3年

        pm.setNextHandler(gm);//设置传递顺序
        pm.process(leaveRequest);
    }
}

运行结果:

------

项目经理转交总经理 张三,你通过总经理审批!

------

三、源码中的责任链模式

Filter接口有非常多的实现类,这里挑选doFilter方法中的FilterChain参数来看,Tomcat和SpringSecurity中都用到责任链模式:

进入第一个,过滤器链 ApplicationFilterChain 的关键代码如下,过滤器链实际是一个 ApplicationFilterConfig 数组:

代码语言:javascript
复制
final class ApplicationFilterChain implements FilterChain, CometFilterChain {
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; // 过滤器链
    private Servlet servlet = null; // 目标
    // ...

    @Override
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if( Globals.IS_SECURITY_ENABLED ) {
            // ...
        } else {
            internalDoFilter(request,response); // 调用 internalDoFilter 方法
        }
    }

    private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        // Call the next filter if there is one
        if (pos < n) {
            // 从过滤器数组中取出当前过滤器配置,然后下标自增1
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            try {
                filter = filterConfig.getFilter();  // 从过滤器配置中取出该 过滤器对象

                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal = ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
                } else {
                    // 调用过滤器的 doFilter,完成一个过滤器的过滤功能
                    filter.doFilter(request, response, this);
                }
            return;  // 这里很重要,不会重复执行后面的  servlet.service(request, response)
        }

        // 执行完过滤器链的所有过滤器之后,调用 Servlet 的 service 完成请求的处理
        if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
            if( Globals.IS_SECURITY_ENABLED ) {

            } else {
                servlet.service(request, response);
            }
        } else {
            servlet.service(request, response);
        }
    }
    // 省略...
}

这里可以看出ApplicationFilterChain类扮演了抽象处理者角色,doFilter就类似于刚才请假流程里的process方法。

当下标小于过滤器数组长度 n 时,也就是过滤器链未执行完,从数组中取出并调用当前过滤器的 doFilter方法 ,如果下标一直小于n,则循环调用doFilter方法通过嵌套递归的方式来串成一条链。

当最后的过滤器执行完毕,也就是走到最后一个return;时,结束递归调用doFilter。if (pos < n) 为false,调用后面的servlet.service(request, response) 方法。return;这一点在请假流程里也有体现。

参考:

设计模式 | 责任链模式及典型应用

责任链设计模式(过滤器、拦截器)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/12/06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、责任链模式介绍
  • 二、请假示例
  • 三、源码中的责任链模式
相关产品与服务
云顾问
云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档