文章目錄
- 1、責任鏈模式
- 2、spring中的責任鏈模式
- Spring Interceptor
- Servlet Filter
- Netty
1、責任鏈模式
責任鏈模式為請求創建了一個接收者對象的鏈,在這種模式下,通常每個節點都包含對另一個節點者的引用。每個節點針對請求,處理自己感興趣的內容,處理完之后可以結束,也可以向下一個節點傳遞繼續處理;
角色:
- 抽象處理者角色:處理請求的抽象類,定義了處理請求的抽象方法;(抽象類或接口實現);
- 具體處理者角色:處理請求的具體實現類;(持有下家對象的引用);
例:請假流程都是先由本部門審核,根據時間長短再進行下一級審批
//抽象類
public abstract class Handler {/*** 請假天數*/public int maxday;/*** 請假人*/public String name;public Handler(String name, int maxday) {this.maxday = maxday;this.name = name;}private Handler nextHandler;public void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}/*** 處理請假:判斷請假天數,超過本部門限定時間則交由上一級部門*/public final void handlingFakeSlips(int day) {if (this.maxday >= day) {this.agree(day);}else {if (nextHandler != null) {System.out.println(name+":天數已超過我的審批權限,已提交我的上級審批");nextHandler.handlingFakeSlips(day);}else {System.out.println("天數時間過長,準備辭職吧!!!");}}}/*** 審批動作:子類來實現* @param day*/abstract void agree(int day);
}//部門實現類
public class RDDepartment extends Handler{public RDDepartment(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":研發部門請假審批通過,請假天數:" + maxday);}
}//主管實現類
public class Supervisor extends Handler{public Supervisor(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":主管請假審批通過,請假天數:" + maxday);}
}//董事實現類
public class Director extends Handler{public Director(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":請假董事審批通過,請假天數:" + maxday);}
}//組裝鏈
public class HandlerChain {private Handler head;private Handler tail;public HandlerContext(){RDDepartment rdDepartment = new RDDepartment("研發部門",5);Supervisor supervisor = new Supervisor("項目主管",30);Director director = new Director("董事",180);rdDepartment.setNextHandler(supervisor);supervisor.setNextHandler(director);head = rdDepartment;tail = director;}public void doHandler(Integer days){head.handlingFakeSlips(days);}
}//請求
public class Request {public static void main(String[] args) {HandlerChain handlerChain = new HandlerChain();handlerChain.doHandler(360);}
}
優點(when,why):
? 1.發送者與接收者之間的耦合度降低(解耦)
? 2.可以靈活添加新的責任鏈中的對象
缺點:
? 1.不能保證請求一定被接收
? 2.一定程度上影響性能
這種形式很難進行動態新增和調整處理節點,一種比較復雜的控制節點的形式如Netty中的責任鏈模式應用,見下一節
2、spring中的責任鏈模式
Spring Interceptor
回顧springmvc
處理請求的流程:DispatcherServlet
接收到請求后,執行doDispatcher()
方法,流程回顧請求處理流程圖
其中通過HandlerMapping
獲得的是HandlerExecutionChain
對象
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//......HandlerExecutionChain mappedHandler = null;//......mappedHandler = getHandler(processedRequest);//...... HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//......
}protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
HandlerExecutionChain
中包含一個handler
對象(后面匹配能處理handler的適配器對象執行,詳情對應適配器模式中講解),還有一個攔截器列表List<HandlerInterceptor> interceptorList
,所有的實現了HandlerInterceptor
接口的類都會被加載進這個集合中,在請求處理前后分別以責任鏈的形式調用攔截器的preHandle
和postHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {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 applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {HandlerInterceptor[] interceptors = 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);}}
}
這里的鏈是由集合List維護,使用List有序的特性一次調用每個攔截器,通過方法返回的結果判斷是否需要傳遞到下一個攔截器
Servlet Filter
servlet
中Filter
的調用也是通過責任鏈模式,通過FilterChain
作為鏈條的管理者
//FilterChain接口
public interface FilterChain {public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}//FilterChain實現類
public class MockFilterChain implements FilterChain {//......private final List<Filter> filters;//......@Overridepublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {Assert.notNull(request, "Request must not be null");Assert.notNull(response, "Response must not be null");Assert.state(this.request == null, "This FilterChain has already been called!");if (this.iterator == null) {this.iterator = this.filters.iterator();}if (this.iterator.hasNext()) {Filter nextFilter = this.iterator.next();nextFilter.doFilter(request, response, this);}this.request = request;this.response = response;}
}
由上可知,
FilterChain
中管理的List<Filter> filters
即為所有實現了Filter
的過濾器,調用過濾器的時候,通過FilterChain
進行鏈條的調用。
//Filter接口
public interface Filter {default public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)throws IOException, ServletException;default public void destroy() {}
}//Filter實現類
public class AuthFilter extends AuthenticationFilter {//......public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpRequest = toLowerCase((HttpServletRequest)request);String tokenString = httpRequest.getParameter("delegation");if (tokenString != null) {filterChain.doFilter(httpRequest, response);} else {super.doFilter(httpRequest, response, filterChain);}}//......
}
Filter
的doFilter
方法最后調用filterChain.doFilter(httpRequest, response);
即傳遞至下一個Filter進行處理
Netty
Netty中的handler使用了責任鏈模式,但是其中回調過多,責任鏈模式的體現不清晰,參考該文章**Spring中如何使用責任鏈模式**,將責任鏈抽離出來,完成在spring中的調用
該模型中,具有多條鏈,每條鏈屬于不同層級,鏈中節點為
HandlerContext
,HandlerContext
包含相鄰接點的引用,還有Handler
的引用
Pipeline
:為鏈條的管理者,通過Pipeline
來調用責任鏈
HandlerContext
:為鏈條中節點的上下文,它里面有鏈條的前一個節點和后一個節點的HandlerContext
引用
Handler
: 具體的處理程序,與HandlerContext一一對應
我們先僅看Filter事件這一條鏈,整個結構由Pipeline
管理整條鏈
//Pipelie接口
public interface Pipeline {Pipeline fireTaskFiltered();
}//Pipeline實現類
Component("pipeline")
@Scope("prototype")
public class DefaultPipeline implements Pipeline, ApplicationContextAware, InitializingBean {// 創建一個默認的handler,將其注入到首尾兩個節點的HandlerContext中,其作用只是將鏈往下傳遞private static final Handler DEFAULT_HANDLER = new Handler() {};// 將ApplicationContext注入進來的主要原因在于,HandlerContext是prototype類型的,因而需要// 通過ApplicationContext.getBean()方法來獲取其實例private ApplicationContext context;// 創建一個頭結點和尾節點,這兩個節點內部沒有做任何處理,只是默認的將每一層級的鏈往下傳遞,// 這里頭結點和尾節點的主要作用就是用于標志整個鏈的首尾,所有的業務節點都在這兩個節點中間private HandlerContext head;private HandlerContext tail;// 用于業務調用的request對象,其內部封裝了業務數據private Request request;// 用于執行任務的task對象private Task task;// 最初始的業務數據需要通過構造函數傳入,因為這是驅動整個pipeline所需要的數據,// 一般通過外部調用方的參數進行封裝即可public DefaultPipeline(Request request) {this.request = request;}// 這里我們可以看到,每一層級的調用都是通過HandlerContext.invokeXXX(head)的方式進行的,// 也就是說我們每一層級鏈的入口都是從頭結點開始的,當然在某些情況下,我們也需要從尾節點開始鏈// 的調用,這個時候傳入tail即可。// 觸發任務過濾的鏈調用@Overridepublic Pipeline fireTaskFiltered() {HandlerContext.invokeTaskFiltered(head, task);return this;}// 用于往Pipeline中添加節點的方法,讀者朋友也可以實現其他的方法用于進行鏈的維護void addLast(Handler handler) {HandlerContext handlerContext = newContext(handler);tail.prev.next = handlerContext;handlerContext.prev = tail.prev;handlerContext.next = tail;tail.prev = handlerContext;}// 這里通過實現InitializingBean接口來達到初始化Pipeline的目的,可以看到,這里初始的時候// 我們通過ApplicationContext實例化了兩個HandlerContext對象,然后將head.next指向tail節點,// 將tail.prev指向head節點。也就是說,初始時,整個鏈只有頭結點和尾節點。@Overridepublic void afterPropertiesSet() throws Exception {head = newContext(DEFAULT_HANDLER);tail = newContext(DEFAULT_HANDLER);head.next = tail;tail.prev = head;}// 使用默認的Handler初始化一個HandlerContextprivate HandlerContext newContext(Handler handler) {HandlerContext context = this.context.getBean(HandlerContext.class);context.handler = handler;return context;}// 注入ApplicationContext對象@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.context = applicationContext;}
}
Pipeline
的實現類內部除了實現接口的方法,其他方法均為初始化
DEFAULT_HANDLER
:Pipeline
管理一條鏈表,該鏈表的每個節點包含HandlerContext
和Handler
兩個對象, 而鏈表的首和尾兩個節點由Pipeline
自己指定(其他自定義的節點放在首尾兩節點之間),DEFAULT_HANDLER
用來作為首尾節點的Handler
,不起任何作用context
: 由于這些類的作用域均不是單例,所以要使用ApplicationContext.getBean()
方法獲取,所以類實現了ApplicationContextAware
接口的setApplicationContext
方法,用于注入ApplicationContext對象private HandlerContext head, tail
: 為一條鏈的首尾兩個節點,從這兒也可以看出,鏈條的每個節點都是通過HandlerContext
來引用的,HandlerContext
再引用一個Handler
@Component
@Scope("prototype")
public class HandlerContext {HandlerContext prev;HandlerContext next;Handler handler;private Task task;public void fireTaskFiltered(Task task) {invokeTaskFiltered(next(), task);}/*** 處理任務過濾事件*/static void invokeTaskFiltered(HandlerContext ctx, Task task) {if (null != ctx) {try {ctx.handler().filterTask(ctx, task);} catch (Throwable e) {ctx.handler().exceptionCaught(ctx, e);}}}private HandlerContext next() {return next;}private Handler handler() {return handler;}
}
HandlerContext
作為節點,應有前后兩個節點的引用pre
next
,還有具體處理任務的Handler
fireTaskFiltered
方法供Hanndler
調用,將請求傳遞給下一個節點處理(方法實現中區下一個HandlerContext
去執行)
invokeTaskFiltered
靜態方法供Pipeline
和 上一個節點的fireTaskFiltered
方法調用,去執行Handler
的方法
public interface Handler {/*** 查詢到task之后,進行task過濾的邏輯*/default void filterTask(HandlerContext ctx, Task task) {ctx.fireTaskFiltered(task);}
}
Handler
定義了感興趣的事件(暫時只看過濾事件)
Handler
的實現類由我們根據自己的需要去編寫,實現Handler
接口即可
@Component
public class DurationHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("時效性檢驗");ctx.fireTaskFiltered(task);}
}@Component
public class RiskHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("風控攔截");ctx.fireTaskFiltered(task);}
}@Component
public class TimesHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("次數限制檢驗");ctx.fireTaskFiltered(task);}
}
這里我們已經實現了
Pipeline
,HandlerContext
和Handler
,知道這些bean都是被Spring所管理的bean,那么我們接下來的問題主要在于如何進行整個鏈的組裝。這里的組裝方式比較簡單,其主要需要解決兩個問題:
- 對于后續寫業務代碼的人而言,其只需要實現一個
Handler
接口即可,而無需處理與鏈相關的所有邏輯,因而我們需要獲取到所有實現了Handler
接口的bean;- 將實現了
Handler
接口的bean通過HandlerContext
進行封裝,然后將其添加到Pipeline
中。
以上可以由spring完成,通過生命實現接口BeanPostProcessor
的類,實現postProcessAfterInitialization
方法,可以在初始化完Pipeline
后,獲取所有實現了Handler
的bean
,并添加到pipeline
中,spring自動調用該方法
@Component
public class HandlerBeanProcessor implements BeanPostProcessor, ApplicationContextAware {private ApplicationContext context;// 該方法會在一個bean初始化完成后調用,這里主要是在Pipeline初始化完成之后獲取所有實現了// Handler接口的bean,然后通過調用Pipeline.addLast()方法將其添加到pipeline中@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof DefaultPipeline) {DefaultPipeline pipeline = (DefaultPipeline) bean;Map<String, Handler> handlerMap = context.getBeansOfType(Handler.class);handlerMap.forEach((name, handler) -> pipeline.addLast(handler));}return bean;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.context = applicationContext;}
}
postProcessAfterInitialization(Object bean, String beanName)
: 實現了BeanPostProcessor
的該方法,在spring啟動之后沒初始化完成一個Bean
之后,都會調用該方法如此,當初始化完
pipeline
之后,獲取實現了Handler接口的所有實現類,在addLast()
方法中為每一個Handler
初始化一個HandlerContext
,并添加到Pipeline
中
如此下來,整個過程調用如下
@Service
public class ApplicationService {@Autowiredprivate ApplicationContext context;public void mockedClient(Request request) {Pipeline pipeline = newPipeline(request);pipeline.fireTaskFiltered();}private Pipeline newPipeline(Request request) {return context.getBean(DefaultPipeline.class, request);}
}
1、spring項目啟動,加載和初始化
Bean
,當加載到DefaultPipeline
的時候,由于實現了InitializingBean
接口,所以會調用初始化方法afterPropertiesSet()
,為DefaultPipeline
添加兩個首尾節點HandlerContext
;2、當初始化完成
Pipeline
之后,調用postProcessAfterInitialization(Object bean, String beanName)
;3、加載所有實現了
Hnadler
接口的Bean
,并通過pipeline.addLast(Handler handler
方法為每一個handler
創建一個HandlerContext
,并添加到鏈條中;至此形成了一條鏈
4、
service
想執行該鏈的時候,通過有參構造方法,將請求request
傳遞給pipeline
,調用pipeline.fireTaskFiltered()
;5、
pipeline.fireTaskFiltered()
中,會調用HandlerContext
的靜態方法HandlerContext.invokeTaskFiltered(HandlerContext ctx, Task task)
將第一個HandlerContext
傳入去執行,其handler
的filterTask(HandlerContext ctx, Task task)
方法執行具體邏輯6、
handler
的filterTask(HandlerContext ctx, Task task)
方法最后會調用ctx
的invokeTaskFiltered(HandlerContext ctx, Task task)
方法,該方法會使用invokeTaskFiltered(next(), task)
去執行下一個節點ctx.handler().filterTask(ctx, task)
7、直至最后到節點
tail
沒有下一個節點會停止執行;
至此單條鏈的責任鏈模式已完成
在netty中,并不是一條鏈,每一個handler有很多針對不同的事件的處理
在pipeline中有所有的事件,我們相對某一個事件處理是,實現handler的對應方法的處理邏輯,就會在對應層級的鏈中加入該handler,netty多層級代碼責任鏈參考:Spring中如何使用責任鏈模式