責任鏈模式的應用
現有一開發場景,要求設計一個集團採購物品,不同金額交由不同領導審批的流程
現有三級領導,金額在5000以下的,由部門領導審批;金額在10000以下的由院校級領導審批;金額在10000以上的,由校級領導審批。雖然使用 if-else 也能實現改功能,但是代碼的可讀性、可擴展性差,使用責任鏈設計模式,是面向對象的思維方式,符合“開閉原則”,具體實現的代碼如下:
責任鏈調用發起者
public static void main(String[] args) {
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 5001, 11);
DepartmentApprover departmentApprover = new DepartmentApprover("部門審批者 張xx");
CollegeApprover collegeApprover = new CollegeApprover("院系審批者 李xx");
SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("校長審批 王xx");
//設置責任鏈的下一個處理類(該段設置chain的邏輯,也可以在不同的approver子類中設置,但缺點是不易管理)
departmentApprover.nextApprover=collegeApprover;
collegeApprover.nextApprover=schoolMasterApprover;
//爲實現調用chain中任意一個處理類,都可以得到正確的結果,在這裏構建一個環形鏈表
schoolMasterApprover.nextApprover=departmentApprover;
schoolMasterApprover.processRequest(purchaseRequest);//輸出:請求編號:11 當前審批人名:院系審批者 李xx
}
批准人抽象類
public abstract class Approver {
Approver nextApprover;//下一個處理者
String name;
public Approver(String name) {
this.name = name;
}
//交由子類去完善處理審批的邏輯
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
部門審批者、院系審批者、校長審批實現類
public class DepartmentApprover extends Approver{
public DepartmentApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.price<=5000f){
System.out.println("請求編號:" + purchaseRequest.id + " 當前審批人名:" + this.name);
}else {
nextApprover.processRequest(purchaseRequest);
}
}
}
public class CollegeApprover extends Approver {
public CollegeApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.price > 5000f && purchaseRequest.price <= 10000) {
System.out.println("請求編號:" + purchaseRequest.id + " 當前審批人名:" + this.name);
} else {
nextApprover.processRequest(purchaseRequest);
}
}
}
public class SchoolMasterApprover extends Approver {
public SchoolMasterApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.price>10000f){
System.out.println("請求編號:" + purchaseRequest.id + " 當前審批人名:" + this.name);
}else {
nextApprover.processRequest(purchaseRequest);
}
}
}
使用責任鏈的注意事項和細節
- 降低了流程中,調用鏈與鏈中某一節點執行邏輯的耦合度,提高了系統的靈活性
- 當鏈中節點比較多時,系統的性能會受到影響,因此,可以考慮控制最長鏈節點。
設計模式是一種思想,在不同的地方,實現的方式可能不同,但大致的思想都是一致的,都是爲了提高擴展性,可讀性等原因。
責任鏈在 Spring MVC 的 HandlerExecutionChain 中的應用源碼解析
在 Spring MVC 的 DispatcherServlet 類中,有如下的主要代碼邏輯:
doDispatch方法中,構建了 interceptor 執行鏈,將請求分配給 preHandle 執行,當 preHandle 方法返回爲 false時,會調用到 afterCompletion 執行,最後調用 postHandle 方法。
protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerExecutionChain mappedHandler = null;
mappedHandler = getHandler(processedRequest);
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//當上面的 applyPreHandle 返回false時,繼續向下執行到
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
//調用攔截器的 interceptor.preHandle
boolean applyPreHandle (HttpServletRequest request, HttpServletResponse response) {
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 triggerAfterCompletion (HttpServletRequest request, HttpServletResponse response,Exception ex){
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
}
//調用攔截器的 interceptor.postHandle
void applyPostHandle (HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv){
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
- HandlerExecutionChain 本身不會執行 interceptor 的邏輯,只是將請求分配給 chain 上註冊的 interceptor 執行;
- HandlerExecutionChain 維護了 HandlerInterceptor 的集合。
監聽器(listener)、過濾器(filter)、攔截器(interceptor)的執行順序參考
SpingMvc的整體執行流程參考