目錄
概述
責任鏈模式主要就是爲了對請求發送者和請求接收者進行解耦,屏蔽請求的處理過程,當一個請求發出後,作爲請求者可以不用知道到底是由誰來處理的,這是責任鏈模式的核心。換句話來說就是:要麼承擔責任作出響應,要麼向下傳遞請求,當然也可以不作任何處理。在責任鏈模式下,每一個接收者都擁有下一個接收者的引用,所有接收者便組成了一條鏈,但是責任鏈模式不能保證請求一定被處理
應用實例
1、“擊鼓傳花”小遊戲
2、java web中Apache Tomcat對Encoding的處理,Struts2的攔截器,jsp servlet的Filter
優點
1、降低耦合度,它將請求的發送者和接收者解耦
2、簡化了對象,使得對象不需要知道鏈的結構
3、增強給對象指派職責的靈活性,通過改變鏈內的成員或者調動它們的次序,允許動態的新增或者刪除責任
4、增加新的請求處理類很方便
缺點
1、不能保證請求一定被接收
2、調試起來比較麻煩
責任鏈代碼示例
代碼示例以多個日誌記錄器作爲請求接收者,這多個日誌記錄器組成一條鏈,然後由第一個日誌記錄器首先接收請求進行判斷:是自己處理還是說將該請求繼續傳遞給下一接收者。
1、首先新建一個抽象日誌類,包含日誌級別以及下一個日誌記錄器的引用:
abstract class Logger {
public static final int INFO = 1;
public static final int DEBUG = 2;
public static final int ERROR = 3;
/**
* 當前日誌記錄器的級別,用來判斷當前記錄器是否有權限
*/
protected int level;
/**
* 下一個接收者的引用
*/
protected Logger nextLogger;
public void setNextLogger(Logger nextLogger) {
this.nextLogger = nextLogger;
}
/**
* 具體判斷、輸出邏輯
* @param level
* @param msg
*/
public void logMessage(int level, String msg) {
if (this.level <= level) {
write(msg);
}
if (null != nextLogger) {
nextLogger.logMessage(level, msg);
}
}
public abstract void write(String msg);
}
2、新建具體日誌記錄器實現基本接口
/**
* Info日誌實現類
*/
class InfoLogger extends Logger {
public InfoLogger(int level) {
this.level = level;
}
@Override
public void write(String msg) {
System.out.println("Info context:" + msg);
}
}
/**
* Debug日誌實現類
*/
class DebugLogger extends Logger {
public DebugLogger(int level) {
this.level = level;
}
@Override
public void write(String msg) {
System.out.println("Debug context:" + msg);
}
}
/**
* Error日誌實現類
*/
class ErrorLogger extends Logger {
public ErrorLogger(int level) {
this.level = level;
}
@Override
public void write(String msg) {
System.out.println("Error context:" + msg);
}
}
3、創建不同的日誌記錄器並組裝成鏈
public static void main(String[] args) {
// 這裏直接new出三個請求接收者,並組裝成一條鏈
Logger infoLogger = new InfoLogger(Logger.INFO);
Logger debuLogger = new DebugLogger(Logger.DEBUG);
Logger errorLogger = new ErrorLogger(Logger.ERROR);
errorLogger.setNextLogger(debuLogger);
debuLogger.setNextLogger(infoLogger);
// 模擬發送請求給最外層的接收者,請求發送者無需知道該請求是由誰來處理的
errorLogger.logMessage(Logger.INFO, "This is a info message");
errorLogger.logMessage(Logger.DEBUG, "This is a debug message");
errorLogger.logMessage(Logger.ERROR, "This is a error message");
}
4、查看輸出結果
Info context:This is a info message
Debug context:This is a debug message
Info context:This is a debug message
Error context:This is a error message
Debug context:This is a error message
Info context:This is a error message
Filter過濾器簡單原理
過濾器主要是基於函數回調機制實現,同時過濾器也是完全依賴Servlet容器,即只能在容器前後起作用,具體實現的話只要新建一個類並實現Filter接口即可,它自帶了三個方法,起主要作用的便是doFilter,其餘兩個方法見名便知其意。
實現Filter接口後如下:
public class FirstFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 在請求傳遞或執行前業務操作
filterChain.doFilter(servletRequest, servletResponse);
// 在請求傳遞或執行後業務操作
}
@Override
public void destroy() {
}
}
Filter執行過程
Filter過濾器在設計方面便是採用責任鏈設計模式,在一個web應用中,可以根據需求開發多個Filter,這些Filter組合起來便稱之爲一個鏈,根據具體註冊順序進行組裝並決定先調用哪個Filter,當第一個Filter的doFilter方法被調用時,web服務器會創建一個代表Filter鏈的FilterChain對象傳遞給該方法。在doFilter方法中,開發人員如果調用了FilterChain對象的doFilter方法,則web服務器會檢查FilterChain對象中是否還有filter,如果有,則調用第2個filter,如果沒有,則調用目標資源。
Filter源碼
換成官方一點的說法就是所有組裝起來的Filter鏈都放在ApplicationFilterChain的數組中,執行時便先判斷數組中是否還有Filter,如果有則執行對應的Filter,如果沒有則執行servlet實例的service方法傳遞請求。下面我們對照着來看下源碼實現,很簡單:
1、首先看下doFilter方法
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 判斷是否開啓安全驗證,若開啓則執行安全檢查,最後仍是調用internalDoFilter方法
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 {
// 具體的執行邏輯
internalDoFilter(request,response);
}
}
2、然後進到internalDoFilter方法
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// pos:是當前Filter鏈中所執行到的下標 n:Filter鏈的總數
// 如果數組中還有未執行的Filter則繼續執行
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
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.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;
}
// 如果到這邊就說明Filter鏈已經全部執行完畢,接下來需要執行Servlet實例
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);
}
}
}