1、OA系统需求
学校OA
系统的采购审批项目:需求是
1)采购员采购教学器材
2)如果金额 小于等于5000
,由教学主任审批 (0 <=x<= 5000
)
3)如果金额 小于等于10000
,由院长审批 (5000 <x<= 10000
)
4)如果金额 小于等于30000
,由副校长审批 (10000 <x<= 30000
)
5)如果金额 超过30000
以上,有校长审批
请设计程序完成采购审批项目
2、传统方案问题
传统方式是:
- 接收到一个采购请求后,根据采购金额来调用对应的
Approver
(审批人)完成审批。
传统方式的问题分析:
- 客户端这里会使用到分支判断(比如
switch
)来对不同的采购请求处理,这样就存在如下问题 - (1)如果各个级别的人员审批金额发生变化,在客户端的也需要变化
- (2)客户端必须明确的知道有多少个审批级别和访问
这样对一个采购请求进行处理和Approver
(审批人)就存在强耦合关系,不利于代码的扩展和维护
解决方案 =》职责链模式
3、基本介绍
-
职责链模式(
chain of Responsibility Pattern
) ,- 又叫责任链模式,为请求创建了一个 接 收 者 对 象 的 链 ( 简 单 示 意 图 ) \color{red}接收者对象的链(简单示意图) 接收者对象的链(简单示意图)。
- 这种模式对请求的发送者和接收者进行解耦。
-
职责链模式通常每个接收者都包含对另一个接收者的引用。
- 如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
-
这种类型的设计模式属于行为型模式
-
类似于类加载
4、原理类图
如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
A
处理不了就交给B
Handler
:
- 抽象的处理者,定义了一个处理请求的接口,同时含有另外
Handler
ConcreteHandlerA,B
- 是具体的处理者,处理他自己负责的请求,可以访问它的后继者(即下一个处理者),如果可以处理当前请求,则处理,否则就将该请求交给 后继者去处理,从而形成一个职责链
Request
- 含义很多属性,表示一个请求
5、案例解决
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源码
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 Web
中Tomcat
对Encoding
的处理、拦截器
- 多级请求、请假/加薪等审批流程、