26.职责链模式

1、OA系统需求

学校OA系统的采购审批项目:需求是

1)采购员采购教学器材

2)如果金额 小于等于5000,由教学主任审批 (0 <=x<= 5000

3)如果金额 小于等于10000,由院长审批 (5000 <x<= 10000)

4)如果金额 小于等于30000,由副校长审批 (10000 <x<= 30000)

5)如果金额 超过30000以上,有校长审批

请设计程序完成采购审批项目

2、传统方案问题

image-20221104103538186

传统方式是:

  • 接收到一个采购请求后,根据采购金额来调用对应的Approver(审批人)完成审批。

传统方式的问题分析:

  • 客户端这里会使用到分支判断(比如 switch)来对不同的采购请求处理,这样就存在如下问题
  • (1)如果各个级别的人员审批金额发生变化,在客户端的也需要变化
  • (2)客户端必须明确的知道有多少个审批级别和访问

这样对一个采购请求进行处理和Approver(审批人)就存在强耦合关系,不利于代码的扩展和维护

解决方案 =》职责链模式

3、基本介绍

  • 职责链模式(chain of Responsibility Pattern) ,

    • 又叫责任链模式,为请求创建了一个 接 收 者 对 象 的 链 ( 简 单 示 意 图 ) \color{red}接收者对象的链(简单示意图) ()
    • 这种模式对请求的发送者和接收者进行解耦
  • 职责链模式通常每个接收者都包含对另一个接收者的引用

    • 如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
  • 这种类型的设计模式属于行为型模式

  • 类似于类加载

4、原理类图

image-20221104110103770

如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

  • A处理不了就交给B

Handler

  • 抽象的处理者,定义了一个处理请求的接口,同时含有另外Handler

ConcreteHandlerA,B

  • 是具体的处理者,处理他自己负责的请求,可以访问它的后继者(即下一个处理者),如果可以处理当前请求,则处理,否则就将该请求交给 后继者去处理,从而形成一个职责链

Request

  • 含义很多属性,表示一个请求

5、案例解决

image-20221104111732632

Client

public class Client {
    public static void main(String[] args) {
        //创建一个请求
        new PurchaseRequest(1,0,1000,1);

        //创建相关的审批人
        DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
        CollegeApprover collegeApprover = new CollegeApprover("李院长");
        ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校");
        SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");

        //需要将各个审批级别的下一个设置好(处理人构成环形: 可能直接从校长或中间的某个处理人开始寻找,最后总能找到一个人进行处理)
        departmentApprover.setApprover(collegeApprover);
        collegeApprover.setApprover(viceSchoolMasterApprover);
        viceSchoolMasterApprover.setApprover(schoolMasterApprover);
        schoolMasterApprover.setApprover(departmentApprover);

        //从任何人开始都能找到请求
        departmentApprover.processRequest(new PurchaseRequest());
        schoolMasterApprover.processRequest(new PurchaseRequest());

    }
}

Approver 处理者

public abstract class Approver {
    /**
     * 下一个处理者
     */
    protected Approver approver;
    /**
     * 名字
     */
    protected String name;

    public Approver(String name) {
        this.name = name;
    }

    public void setApprover(Approver approver) {
        this.approver = approver;
    }

    /**
     * 处理审批请求的方法,得到一个请求,
     * 处理是子类完成,因此该方法做成抽象
     */
    public abstract void processRequest(PurchaseRequest purchaseRequest);
}

DepartmentApprover 系主任

public class DepartmentApprover extends Approver {
    public DepartmentApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice() <= 5000) {
            System.out.println("请求编号id=" + purchaseRequest.getId() + "被"
                    + this.name + "处理");
        } else {//处理不了,让下一个后继者如初请求
            approver.processRequest(purchaseRequest);
        }
    }

}

CollegeApprover 院主任

public class CollegeApprover extends Approver {
    public CollegeApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (5000 < purchaseRequest.getPrice() && purchaseRequest.getPrice() <= 10000) {
            System.out.println("请求编号id=" + purchaseRequest.getId() + "被"
                    + this.name + "处理");
        } else {//处理不了,让下一个后继者如初请求
            approver.processRequest(purchaseRequest);
        }
    }

}

6、职责链模式-SpringMVC源码

image-20221008004914974

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                mappedHandler = this.getHandler(processedRequest);//这里返回一条职责链
                if (mappedHandler == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }

                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                //通过执行链的applyPreHandle执行相应拦截器的相关方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) { 
                    return;
                }

                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                this.applyDefaultViewName(processedRequest, mv);
                
                //通过执行链的applyPostHandle执行相应拦截器的相关方法
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }

            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

自己实现拦截器接口,注册到SpringMVC中。

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

通过执行链的applyPreHandle/applyPostHandle寻找相应拦截器(自己实现的拦截器)

  • 并执行相关方法

    • preHandle 再请求被处理之前执行操作,预处理

    • postHandle 在请求被处理之后,但结果还没有渲染之前进行操作,可以改变响应结果,后处理

    • afterCompletion 有的请求响应结束后执行善后工作,清理对象,关闭资源

  • 例如:登录拦截

    • public class LoginInterceptor implements HandlerInterceptor {
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              //是否登录过的判断
              if(request.getSession(false).getAttribute("users")==null){
                  //此时就是没有登录,大会到登陆页面,并给出提示
                  request.setAttribute("msg","您还没有登录请先去登录");
                  request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
                  return false;
              }
              return true;//放行请求
          }
      }
      

HandlerExecutionChain

public class HandlerExecutionChain {
    //.......
    
    /**
     
      if (!mappedHandler.applyPreHandle(processedRequest, response)) { 
      		//直接结束doDispatch方法
            return;
      }
     */
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                //如果被拦截且返回false,取反为true,例如:您还没有登录请先去登录,则执行相应的请求结束方法
                if (!interceptor.preHandle(request, response, this.handler)) {
                    //有的请求响应结束后执行善后工作,清理对象,关闭资源
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    //返回false,直接返回
                    return false;
                }
            }
        }
    }
    
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = this.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 var8) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
                }
            }
        }

    }
    
    
    
    //mappedHandler.applyPostHandle(processedRequest, response, mv);
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.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);
            }
        }

	}
    
    
}

说明

  • 通过拦截器链 寻找相应的拦截器并执行了 拦截器相关方法 interceptor.preHandler 等等

  • 在处理SpringMVC请求时,使用到职责链模式还是用到适配器模式

  • HandlerExcutionChain主要负责的时请求拦截器的执行和请求处理,

    • 但是他本身不处理请求
    • 只是将请求分配给链上注册的处理器执行,这是职责链实现方式
    • 减少了职责链本身与处理逻辑之间的耦合,规范了一个处理流程
  • HandlerExcutionChain维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器

7、注意事项与细节

  • 请求和处理分开,实现解耦,提高系统的灵活性

  • 简化了对象使对象不需要知道链的结构

  • 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,

    • 一般通过在Handler中设置一个最大节点数量,
    • setNext()方法中判断是否已经超过阀值超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
  • 调试不方便。

    • 采用了类似递归的方式,调试时逻辑可能比较复杂
  • 最佳应用场景:

    • 有多个对象可以处理同一个请求时,
    • 比如:
      • 多级请求、请假/加薪等审批流程、Java WebTomcatEncoding的处理、拦截器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值