顧名思義,責任鏈模式(Chain of Responsibility Pattern)爲請求建立了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬於行爲型模式。在這種模式中,一般每一個接收者都包含對另外一個接收者的引用。若是一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。html
意圖 避免請求發送者與接收者耦合在一塊兒,讓多個對象都有可能接收請求,將這些對象鏈接成一條鏈,而且沿着這條鏈傳遞請求,直到有對象處理它爲止。java
主要解決 職責鏈上的處理者負責處理請求,客戶只須要將請求發送到職責鏈上便可,無須關心請求的處理細節和請求的傳遞,因此職責鏈將請求的發送者和請求的處理者解耦了。git
什麼時候使用 在處理消息的時候以過濾不少道。github
如何解決 攔截的類都實現統一接口。web
關鍵代碼 Handler 裏面聚合它本身,在 handleRequest 裏判斷是否合適,若是沒達到條件則向下傳遞。segmentfault
純責任鏈與不純責任鏈設計模式
主要角色安全
Handler(抽象處理者): 定義一個處理請求的接口,提供對後續處理者的引用app
ConcreteHandler(具體處理者): 抽象處理者的子類,處理用戶請求,可選將請求處理掉仍是傳給下家;在具體處理者中能夠訪問鏈中下一個對象,以便請求的轉發jsp
應用實例
一、紅樓夢中的"擊鼓傳花"。
二、JS 中的事件冒泡。
三、JAVA WEB 中 Apache Tomcat 對 Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。
優勢
一、下降耦合度。它將請求的發送者和接收者解耦。
二、簡化了對象。使得對象不須要知道鏈的結構。
三、加強給對象指派職責的靈活性。經過改變鏈內的成員或者調動它們的次序,容許動態地新增或者刪除責任。
四、增長新的請求處理類很方便。
缺點
一、不能保證請求必定被接收。
二、系統性能將受到必定影響,並且在進行代碼調試時不太方便,可能會形成循環調用。
三、可能不容易觀察運行時的特徵,有礙於除錯。
使用場景
一、有多個對象能夠處理同一個請求,具體哪一個對象處理該請求由運行時刻自動肯定。
二、在不明確指定接收者的狀況下,向多個對象中的一個提交一個請求。
三、可動態指定一組對象處理請求。
travel包裏主要對出行方式的責任鏈模式。跟進用戶身上的錢,在優先級如飛機->火車->大巴的順序下選擇對應的出行模式。
public class Application {
public static void main(String[] args) {
Handler planeHandler = new PlaneHandler();
Handler trainHandler = new TrainHandler();
Handler busHandler = new BusHandler();
planeHandler.setNext(trainHandler);
trainHandler.setNext(busHandler);
planeHandler.handleRequest("老王", 40d);
planeHandler.handleRequest("張三", 140d);
planeHandler.handleRequest("李四", 240d);
planeHandler.handleRequest("吳老五", 340d);
}
}
複製代碼
抽象處理
@Data
public abstract class Handler {
/** * 下一個鏈節點 */
protected Handler next;
public abstract void handleRequest(String name, Double wallet);
}
複製代碼
具體的處理者(飛機、火車、大巴)
@Slf4j
public class PlaneHandler extends Handler {
private double price = 280d;
@Override
public void handleRequest(String name, Double wallet) {
if (price <= wallet) {
log.info("{}身上的錢能夠坐飛機。", name);
return;
}
if (Objects.nonNull(next)) {
next.handleRequest(name, wallet);
return;
}
log.info("{}錢不夠,只能徒步啦", name);
}
}
複製代碼
@Slf4j
public class TrainHandler extends Handler {
private double price = 149.99d;
@Override
public void handleRequest(String name, Double wallet) {
if (price <= wallet) {
log.info("{}身上的錢只能坐火車。", name);
return;
}
if (Objects.nonNull(next)) {
next.handleRequest(name, wallet);
return;
}
log.info("{}錢不夠,只能徒步啦", name);
}
}
複製代碼
@Slf4j
public class BusHandler extends Handler {
private double price = 59.99d;
@Override
public void handleRequest(String name, Double wallet) {
if (price <= wallet) {
log.info("{}身上的錢只能坐大巴。", name);
return;
}
if (Objects.nonNull(next)) {
next.handleRequest(name, wallet);
return;
}
log.info("{}錢不夠,只能徒步啦", name);
}
}
複製代碼
travel2包是對travel包的從新寫法。
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
Handler planeHandler = new PlaneHandler();
Handler trainHandler = new TrainHandler();
Handler busHandler = new BusHandler();
chain.addHandler(planeHandler);
chain.addHandler(trainHandler);
chain.addHandler(busHandler);
chain.handle("老王", 40d);
chain.handle("張三", 140d);
chain.handle("李四", 240d);
chain.handle("吳老五", 340d);
}
}
複製代碼
抽象處理者
public interface Handler {
void handleRequest(String name, Double wallet, HandlerChain chain);
}
複製代碼
具體處理者(飛機、火車、大巴)
@Slf4j
public class PlaneHandler implements Handler {
private double price = 280d;
@Override
public void handleRequest(String name, Double wallet, HandlerChain chain) {
if (price <= wallet) {
log.info("{}身上的錢能夠坐飛機。", name);
chain.reuse();
return;
}
chain.handle(name, wallet);
}
}
複製代碼
@Slf4j
public class TrainHandler implements Handler {
private double price = 149.99d;
@Override
public void handleRequest(String name, Double wallet, HandlerChain chain) {
if (price <= wallet) {
log.info("{}身上的錢只能坐火車。", name);
chain.reuse();
return;
}
chain.handle(name, wallet);
}
}
複製代碼
@Slf4j
public class BusHandler implements Handler {
private double price = 59.99d;
@Override
public void handleRequest(String name, Double wallet, HandlerChain chain) {
if (price <= wallet) {
log.info("{}身上的錢只能坐大巴。", name);
chain.reuse();
return;
}
chain.handle(name, wallet);
}
}
複製代碼
責任鏈管理者
@Slf4j
public class HandlerChain {
private List<Handler> handlerList = new ArrayList<>();
/** * 維護當前鏈上位置 */
private int pos;
/** * 鏈的長度 */
private int handlerLength;
public void addHandler(Handler handler) {
handlerList.add(handler);
handlerLength = handlerList.size();
}
public void handle(String name, double wallet) {
if (CollectionUtils.isEmpty(handlerList)) {
log.error("有錢,但沒提供服務,{}也估計就只能步行了。", name);
return;
}
if (pos >= handlerLength) {
log.error("身上錢不夠,{}也估計就只能步行了。", name);
reuse();
return;
}
Handler handler = handlerList.get(pos++);
if (Objects.isNull(handler)) {
log.error("假服務,{}也估計就只能步行了。", name);
reuse();
return;
}
handler.handleRequest(name, wallet, this);
}
/** * 鏈從新使用 */
public void reuse() {
pos = 0;
}
}
複製代碼
待補充...
補充補充遺留的Filter過濾器中的責任鏈處理。
本次主要是對Tomcat中的Filter處理簡單的梳理,若有不正確的地方,還望指出來,你們互勉,共進。
老項目你們能夠在web.xml中配置filter,現使用Springboot後,也有兩種配置filter方式,經過建立FilterRegistrationBean的方式和經過註解@WebFilter+@ServletComponentScan的方式。
三個主要的角色
FIlter,很少介紹了。
FilterChain servlet容器提供的開發調用鏈的過濾請求的資源。經過調用下一個filter實現過濾,在總體鏈上。
FilterConfig filter的配置器,在servlet容器在Filter初始化的時候傳遞信息。
具體的filter,主要說說Spring中的兩個抽象Filter,GenericFilterBean和OncePerRequestFilter。
前者主要是作init和destroy的操做,重點仍是init方法,destroy只是空實現而已。
後者主要是作真正的doFilter操做,也是咱們在Spring中建立Filter一般繼承的。
而ApplicationFilterChain就算Tomcat中的FilterChain實現。
/** * The int which is used to maintain the current position * in the filter chain. */
private int pos = 0;
/** * The int which gives the current number of filters in the chain. */
private int n = 0;
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
//安全相關的,暫不關注
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
//真正的doFilter
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
//pos 調用鏈中當前鏈接點所在的位置
//n 調用鏈總節點長度
// Call the next filter if there is one
if (pos < n) {
//對節點進行自增 pos++
ApplicationFilterConfig filterConfig = filters[pos++];
try {
//當前節點小於總長度後,從filter配置類中取出filter
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
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 {
//真正的filter
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
//到了調用鏈結尾處,就真正調用servlet實例的servlet.service(request, response);
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
/** * Prepare for reuse of the filters and wrapper executed by this chain. * 重複使用filter調用鏈,pos重設爲0 */
void reuse() {
pos = 0;
}
複製代碼
重點從ApplicationFilterChain中挑出幾個重要的方法拿出來分析下Filter的調用鏈,其實還有幾處沒有具體講到,ApplicationFilterChain是合適建立的,Filter是怎麼加入到ApplicationFilterChain中的。這涉及到Tomcat是怎樣加載Content的,下次分析Tomcat的時候,再來具體分析,它是如何運做的,如何加載web.xml。
Filter、FilterConfig、FilterChain|菜鳥教程