JAVA設(shè)計(jì)模式之責(zé)任鏈模式詳解
在閻宏博士的《JAVA與模式》一書(shū)中開(kāi)頭是這樣描述責(zé)任鏈(Chain of Responsibility)模式的:
責(zé)任鏈模式是一種對(duì)象的行為模式。在責(zé)任鏈模式里,很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而連接起來(lái)形成一條鏈。請(qǐng)求在這個(gè)鏈上傳遞,直到鏈上的某一個(gè)對(duì)象決定處理此請(qǐng)求。發(fā)出這個(gè)請(qǐng)求的客戶(hù)端并不知道鏈上的哪一個(gè)對(duì)象最終處理這個(gè)請(qǐng)求,這使得系統(tǒng)可以在不影響客戶(hù)端的情況下動(dòng)態(tài)地重新組織和分配責(zé)任。
從擊鼓傳花談起
擊鼓傳花是一種熱鬧而又緊張的飲酒游戲。在酒宴上賓客依次坐定位置,由一人擊鼓,擊鼓的地方與傳花的地方是分開(kāi)的,以示公正。開(kāi)始擊鼓時(shí),花束就開(kāi)始依次傳遞,鼓聲一落,如果花束在某人手中,則該人就得飲酒。
比如說(shuō),賈母、賈赦、賈政、賈寶玉和賈環(huán)是五個(gè)參加擊鼓傳花游戲的傳花者,他們組成一個(gè)環(huán)鏈。擊鼓者將花傳給賈母,開(kāi)始傳花游戲。花由賈母?jìng)鹘o賈赦,由賈赦傳給賈政,由賈政傳給賈寶玉,又賈寶玉傳給賈環(huán),由賈環(huán)傳回給賈母,如此往復(fù),如下圖所示。當(dāng)鼓聲停止時(shí),手中有花的人就得執(zhí)行酒令。
擊鼓傳花便是責(zé)任鏈模式的應(yīng)用。責(zé)任鏈可能是一條直線(xiàn)、一個(gè)環(huán)鏈或者一個(gè)樹(shù)結(jié)構(gòu)的一部分。
責(zé)任鏈模式的結(jié)構(gòu)
下面使用了一個(gè)責(zé)任鏈模式的最簡(jiǎn)單的實(shí)現(xiàn)。
責(zé)任鏈模式涉及到的角色如下所示:
● 抽象處理者(Handler)角色:定義出一個(gè)處理請(qǐng)求的接口。如果需要,接口可以定義 出一個(gè)方法以設(shè)定和返回對(duì)下家的引用。這個(gè)角色通常由一個(gè)Java抽象類(lèi)或者Java接口實(shí)現(xiàn)。上圖中Handler類(lèi)的聚合關(guān)系給出了具體子類(lèi)對(duì)下家的引用,抽象方法handleRequest()規(guī)范了子類(lèi)處理請(qǐng)求的操作。
● 具體處理者(ConcreteHandler)角色:具體處理者接到請(qǐng)求后,可以選擇將請(qǐng)求處理掉,或者將請(qǐng)求傳給下家。由于具體處理者持有對(duì)下家的引用,因此,如果需要,具體處理者可以訪(fǎng)問(wèn)下家。
源代碼
抽象處理者角色
public abstract class Handler {
/**
* 持有后繼的責(zé)任對(duì)象
*/
protected Handler successor;
/**
* 示意處理請(qǐng)求的方法,雖然這個(gè)示意方法是沒(méi)有傳入?yún)?shù)的
* 但實(shí)際是可以傳入?yún)?shù)的,根據(jù)具體需要來(lái)選擇是否傳遞參數(shù)
*/
public abstract void handleRequest();
/**
* 取值方法
*/
public Handler getSuccessor() {
return successor;
}
/**
* 賦值方法,設(shè)置后繼的責(zé)任對(duì)象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
}
具體處理者角色
public class ConcreteHandler extends Handler {
/**
* 處理方法,調(diào)用此方法處理請(qǐng)求
*/
@Override
public void handleRequest() {
/**
* 判斷是否有后繼的責(zé)任對(duì)象
* 如果有,就轉(zhuǎn)發(fā)請(qǐng)求給后繼的責(zé)任對(duì)象
* 如果沒(méi)有,則處理請(qǐng)求
*/
if(getSuccessor() != null)
{
System.out.println("放過(guò)請(qǐng)求");
getSuccessor().handleRequest();
}else
{
System.out.println("處理請(qǐng)求");
}
}
}
客戶(hù)端類(lèi)
public class Client {
public static void main(String[] args) {
//組裝責(zé)任鏈
Handler handler1 = new ConcreteHandler();
Handler handler2 = new ConcreteHandler();
handler1.setSuccessor(handler2);
//提交請(qǐng)求
handler1.handleRequest();
}
}
可以看出,客戶(hù)端創(chuàng)建了兩個(gè)處理者對(duì)象,并指定第一個(gè)處理者對(duì)象的下家是第二個(gè)處理者對(duì)象,而第二個(gè)處理者對(duì)象沒(méi)有下家。然后客戶(hù)端將請(qǐng)求傳遞給第一個(gè)處理者對(duì)象。
由于本示例的傳遞邏輯非常簡(jiǎn)單:只要有下家,就傳給下家處理;如果沒(méi)有下家,就自行處理。因此,第一個(gè)處理者對(duì)象接到請(qǐng)求后,會(huì)將請(qǐng)求傳遞給第二個(gè)處理者對(duì)象。由于第二個(gè)處理者對(duì)象沒(méi)有下家,于是自行處理請(qǐng)求?;顒?dòng)時(shí)序圖如下所示。
使用場(chǎng)景
來(lái)考慮這樣一個(gè)功能:申請(qǐng)聚餐費(fèi)用的管理。
很多公司都是這樣的福利,就是項(xiàng)目組或者是部門(mén)可以向公司申請(qǐng)一些聚餐費(fèi)用,用于組織項(xiàng)目組成員或者是部門(mén)成員進(jìn)行聚餐活動(dòng)。
申請(qǐng)聚餐費(fèi)用的大致流程一般是:由申請(qǐng)人先填寫(xiě)申請(qǐng)單,然后交給領(lǐng)導(dǎo)審批,如果申請(qǐng)批準(zhǔn)下來(lái),領(lǐng)導(dǎo)會(huì)通知申請(qǐng)人審批通過(guò),然后申請(qǐng)人去財(cái)務(wù)領(lǐng)取費(fèi)用,如果沒(méi)有批準(zhǔn)下來(lái),領(lǐng)導(dǎo)會(huì)通知申請(qǐng)人審批未通過(guò),此事也就此作罷。
不同級(jí)別的領(lǐng)導(dǎo),對(duì)于審批的額度是不一樣的,比如,項(xiàng)目經(jīng)理只能審批500元以?xún)?nèi)的申請(qǐng);部門(mén)經(jīng)理能審批1000元以?xún)?nèi)的申請(qǐng);而總經(jīng)理可以審核任意額度的申請(qǐng)。
也就是說(shuō),當(dāng)某人提出聚餐費(fèi)用申請(qǐng)的請(qǐng)求后,該請(qǐng)求會(huì)經(jīng)由項(xiàng)目經(jīng)理、部門(mén)經(jīng)理、總經(jīng)理之中的某一位領(lǐng)導(dǎo)來(lái)進(jìn)行相應(yīng)的處理,但是提出申請(qǐng)的人并不知道最終會(huì)由誰(shuí)來(lái)處理他的請(qǐng)求,一般申請(qǐng)人是把自己的申請(qǐng)?zhí)峤唤o項(xiàng)目經(jīng)理,或許最后是由總經(jīng)理來(lái)處理他的請(qǐng)求。
可以使用責(zé)任鏈模式來(lái)實(shí)現(xiàn)上述功能:當(dāng)某人提出聚餐費(fèi)用申請(qǐng)的請(qǐng)求后,該請(qǐng)求會(huì)在 項(xiàng)目經(jīng)理—〉部門(mén)經(jīng)理—〉總經(jīng)理 這樣一條領(lǐng)導(dǎo)處理鏈上進(jìn)行傳遞,發(fā)出請(qǐng)求的人并不知道誰(shuí)會(huì)來(lái)處理他的請(qǐng)求,每個(gè)領(lǐng)導(dǎo)會(huì)根據(jù)自己的職責(zé)范圍,來(lái)判斷是處理請(qǐng)求還是把請(qǐng)求交給更高級(jí)別的領(lǐng)導(dǎo),只要有領(lǐng)導(dǎo)處理了,傳遞就結(jié)束了。
需要把每位領(lǐng)導(dǎo)的處理獨(dú)立出來(lái),實(shí)現(xiàn)成單獨(dú)的職責(zé)處理對(duì)象,然后為它們提供一個(gè)公共的、抽象的父職責(zé)對(duì)象,這樣就可以在客戶(hù)端來(lái)動(dòng)態(tài)地組合職責(zé)鏈,實(shí)現(xiàn)不同的功能要求了。
源代碼
抽象處理者角色類(lèi)
public abstract class Handler {
/**
* 持有下一個(gè)處理請(qǐng)求的對(duì)象
*/
protected Handler successor = null;
/**
* 取值方法
*/
public Handler getSuccessor() {
return successor;
}
/**
* 設(shè)置下一個(gè)處理請(qǐng)求的對(duì)象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
/**
* 處理聚餐費(fèi)用的申請(qǐng)
* @param user 申請(qǐng)人
* @param fee 申請(qǐng)的錢(qián)數(shù)
* @return 成功或失敗的具體通知
*/
public abstract String handleFeeRequest(String user , double fee);
}
具體處理者角色
public class ProjectManager extends Handler {
@Override
public String handleFeeRequest(String user, double fee) {
String str = "";
//項(xiàng)目經(jīng)理權(quán)限比較小,只能在500以?xún)?nèi)
if(fee < 500)
{
//為了測(cè)試,簡(jiǎn)單點(diǎn),只同意張三的請(qǐng)求
if("張三".equals(user))
{
str = "成功:項(xiàng)目經(jīng)理同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
}else
{
//其他人一律不同意
str = "失敗:項(xiàng)目經(jīng)理不同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
}
}else
{
//超過(guò)500,繼續(xù)傳遞給級(jí)別更高的人處理
if(getSuccessor() != null)
{
return getSuccessor().handleFeeRequest(user, fee);
}
}
return str;
}
}
public class DeptManager extends Handler {
@Override
public String handleFeeRequest(String user, double fee) {
String str = "";
//部門(mén)經(jīng)理的權(quán)限只能在1000以?xún)?nèi)
if(fee < 1000)
{
//為了測(cè)試,簡(jiǎn)單點(diǎn),只同意張三的請(qǐng)求
if("張三".equals(user))
{
str = "成功:部門(mén)經(jīng)理同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
}else
{
//其他人一律不同意
str = "失敗:部門(mén)經(jīng)理不同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
}
}else
{
//超過(guò)1000,繼續(xù)傳遞給級(jí)別更高的人處理
if(getSuccessor() != null)
{
return getSuccessor().handleFeeRequest(user, fee);
}
}
return str;
}
}
public class GeneralManager extends Handler {
@Override
public String handleFeeRequest(String user, double fee) {
String str = "";
//總經(jīng)理的權(quán)限很大,只要請(qǐng)求到了這里,他都可以處理
if(fee >= 1000)
{
//為了測(cè)試,簡(jiǎn)單點(diǎn),只同意張三的請(qǐng)求
if("張三".equals(user))
{
str = "成功:總經(jīng)理同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
}else
{
//其他人一律不同意
str = "失敗:總經(jīng)理不同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
}
}else
{
//如果還有后繼的處理對(duì)象,繼續(xù)傳遞
if(getSuccessor() != null)
{
return getSuccessor().handleFeeRequest(user, fee);
}
}
return str;
}
}
客戶(hù)端類(lèi)
public class Client {
public static void main(String[] args) {
//先要組裝責(zé)任鏈
Handler h1 = new GeneralManager();
Handler h2 = new DeptManager();
Handler h3 = new ProjectManager();
h3.setSuccessor(h2);
h2.setSuccessor(h1);
//開(kāi)始測(cè)試
String test1 = h3.handleFeeRequest("張三", 300);
System.out.println("test1 = " + test1);
String test2 = h3.handleFeeRequest("李四", 300);
System.out.println("test2 = " + test2);
System.out.println("---------------------------------------");
String test3 = h3.handleFeeRequest("張三", 700);
System.out.println("test3 = " + test3);
String test4 = h3.handleFeeRequest("李四", 700);
System.out.println("test4 = " + test4);
System.out.println("---------------------------------------");
String test5 = h3.handleFeeRequest("張三", 1500);
System.out.println("test5 = " + test5);
String test6 = h3.handleFeeRequest("李四", 1500);
System.out.println("test6 = " + test6);
}
}
運(yùn)行結(jié)果如下所示:
純的與不純的責(zé)任鏈模式
一個(gè)純的責(zé)任鏈模式要求一個(gè)具體的處理者對(duì)象只能在兩個(gè)行為中選擇一個(gè):一是承擔(dān)責(zé)任,而是把責(zé)任推給下家。不允許出現(xiàn)某一個(gè)具體處理者對(duì)象在承擔(dān)了一部分責(zé)任后又 把責(zé)任向下傳的情況。
在一個(gè)純的責(zé)任鏈模式里面,一個(gè)請(qǐng)求必須被某一個(gè)處理者對(duì)象所接收;在一個(gè)不純的責(zé)任鏈模式里面,一個(gè)請(qǐng)求可以最終不被任何接收端對(duì)象所接收。
純的責(zé)任鏈模式的實(shí)際例子很難找到,一般看到的例子均是不純的責(zé)任鏈模式的實(shí)現(xiàn)。有些人認(rèn)為不純的責(zé)任鏈根本不是責(zé)任鏈模式,這也許是有道理的。但是在實(shí)際的系統(tǒng)里,純的責(zé)任鏈很難找到。如果堅(jiān)持責(zé)任鏈不純便不是責(zé)任鏈模式,那么責(zé)任鏈模式便不會(huì)有太大意義了。
責(zé)任鏈模式在Tomcat中的應(yīng)用
眾所周知Tomcat中的Filter就是使用了責(zé)任鏈模式,創(chuàng)建一個(gè)Filter除了要在web.xml文件中做相應(yīng)配置外,還需要實(shí)現(xiàn)javax.servlet.Filter接口。
public class TestFilter implements Filter{
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
public void destroy() {
}
public void init(FilterConfig filterConfig) throws ServletException {
}
}
使用DEBUG模式所看到的結(jié)果如下
其實(shí)在真正執(zhí)行到TestFilter類(lèi)之前,會(huì)經(jīng)過(guò)很多Tomcat內(nèi)部的類(lèi)。順帶提一下其實(shí)Tomcat的容器設(shè)置也是責(zé)任鏈模式,注意被紅色方框所圈中的類(lèi),從Engine到Host再到Context一直到Wrapper都是通過(guò)一個(gè)鏈傳遞請(qǐng)求。被綠色方框所圈中的地方有一個(gè)名為ApplicationFilterChain的類(lèi),ApplicationFilterChain類(lèi)所扮演的就是抽象處理者角色,而具體處理者角色由各個(gè)Filter扮演。
第一個(gè)疑問(wèn)是ApplicationFilterChain將所有的Filter存放在哪里?
答案是保存在ApplicationFilterChain類(lèi)中的一個(gè)ApplicationFilterConfig對(duì)象的數(shù)組中。
/**
* Filters.
*/
private ApplicationFilterConfig[] filters =
new ApplicationFilterConfig[0];
那ApplicationFilterConfig對(duì)象又是什么呢?
ApplicationFilterConfig是一個(gè)Filter容器。以下是ApplicationFilterConfig類(lèi)的聲明:
/**
* Implementation of a <code>javax.servlet.FilterConfig</code> useful in
* managing the filter instances instantiated when a web application
* is first started.
*
* @author Craig R. McClanahan
* @version $Id: ApplicationFilterConfig.java 1201569 2011-11-14 01:36:07Z kkolinko $
*/
當(dāng)一個(gè)web應(yīng)用首次啟動(dòng)時(shí)ApplicationFilterConfig會(huì)自動(dòng)實(shí)例化,它會(huì)從該web應(yīng)用的web.xml文件中讀取配置的Filter的信息,然后裝進(jìn)該容器。
剛剛看到在ApplicationFilterChain類(lèi)中所創(chuàng)建的ApplicationFilterConfig數(shù)組長(zhǎng)度為零,那它是在什么時(shí)候被重新賦值的呢?
private ApplicationFilterConfig[] filters =
new ApplicationFilterConfig[0];
是在調(diào)用ApplicationFilterChain類(lèi)的addFilter()方法時(shí)。
/**
* The int which gives the current number of filters in the chain.
*/
private int n = 0;
public static final int INCREMENT = 10;
void addFilter(ApplicationFilterConfig filterConfig) {
// Prevent the same filter being added multiple times
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;
if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}
變量n用來(lái)記錄當(dāng)前過(guò)濾器鏈里面擁有的過(guò)濾器數(shù)目,默認(rèn)情況下n等于0,ApplicationFilterConfig對(duì)象數(shù)組的長(zhǎng)度也等于0,所以當(dāng)?shù)谝淮握{(diào)用addFilter()方法時(shí),if (n == filters.length)的條件成立,ApplicationFilterConfig數(shù)組長(zhǎng)度被改變。之后filters[n++] = filterConfig;將變量filterConfig放入ApplicationFilterConfig數(shù)組中并將當(dāng)前過(guò)濾器鏈里面擁有的過(guò)濾器數(shù)目+1。
那ApplicationFilterChain的addFilter()方法又是在什么地方被調(diào)用的呢?
是在ApplicationFilterFactory類(lèi)的createFilterChain()方法中。
public ApplicationFilterChain createFilterChain
(ServletRequest request, Wrapper wrapper, Servlet servlet) {
// get the dispatcher type
DispatcherType dispatcher = null;
if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) {
dispatcher = (DispatcherType) request.getAttribute(DISPATCHER_TYPE_ATTR);
}
String requestPath = null;
Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
// If there is no servlet to execute, return null
if (servlet == null)
return (null);
boolean comet = false;
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
comet = req.isComet();
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
filterChain = new ApplicationFilterChain();
if (comet) {
req.setFilterChain(filterChain);
}
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setSupport
(((StandardWrapper)wrapper).getInstanceSupport());
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return (filterChain);
// Acquire the information we will need to match filter mappings
String servletName = wrapper.getName();
// Add the relevant path-mapped filters to this filter chain
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
boolean isCometFilter = false;
if (comet) {
try {
isCometFilter = filterConfig.getFilter() instanceof CometFilter;
} catch (Exception e) {
// Note: The try catch is there because getFilter has a lot of
// declared exceptions. However, the filter is allocated much
// earlier
Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(t);
}
if (isCometFilter) {
filterChain.addFilter(filterConfig);
}
} else {
filterChain.addFilter(filterConfig);
}
}
// Add filters that match on servlet name second
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
boolean isCometFilter = false;
if (comet) {
try {
isCometFilter = filterConfig.getFilter() instanceof CometFilter;
} catch (Exception e) {
// Note: The try catch is there because getFilter has a lot of
// declared exceptions. However, the filter is allocated much
// earlier
}
if (isCometFilter) {
filterChain.addFilter(filterConfig);
}
} else {
filterChain.addFilter(filterConfig);
}
}
// Return the completed filter chain
return (filterChain);
}
可以將如上代碼分為兩段,51行之前為第一段,51行之后為第二段。
第一段的主要目的是創(chuàng)建ApplicationFilterChain對(duì)象以及一些參數(shù)設(shè)置。
第二段的主要目的是從上下文中獲取所有Filter信息,之后使用for循環(huán)遍歷并調(diào)用filterChain.addFilter(filterConfig);將filterConfig放入ApplicationFilterChain對(duì)象的ApplicationFilterConfig數(shù)組中。
那ApplicationFilterFactory類(lèi)的createFilterChain()方法又是在什么地方被調(diào)用的呢?
是在StandardWrapperValue類(lèi)的invoke()方法中被調(diào)用的。
由于invoke()方法較長(zhǎng),所以將很多地方省略。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
...省略中間代碼
// Create the filter chain for this request
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
...省略中間代碼
filterChain.doFilter(request.getRequest(), response.getResponse());
...省略中間代碼
}
那正常的流程應(yīng)該是這樣的:
在StandardWrapperValue類(lèi)的invoke()方法中調(diào)用ApplicationFilterChai類(lèi)的createFilterChain()方法———>在ApplicationFilterChai類(lèi)的createFilterChain()方法中調(diào)用ApplicationFilterChain類(lèi)的addFilter()方法———>在ApplicationFilterChain類(lèi)的addFilter()方法中給ApplicationFilterConfig數(shù)組賦值。
根據(jù)上面的代碼可以看出StandardWrapperValue類(lèi)的invoke()方法在執(zhí)行完createFilterChain()方法后,會(huì)繼續(xù)執(zhí)行ApplicationFilterChain類(lèi)的doFilter()方法,然后在doFilter()方法中會(huì)調(diào)用internalDoFilter()方法。
以下是internalDoFilter()方法的部分代碼
// Call the next filter if there is one
if (pos < n) {
//拿到下一個(gè)Filter,將指針向下移動(dòng)一位
//pos它來(lái)標(biāo)識(shí)當(dāng)前ApplicationFilterChain(當(dāng)前過(guò)濾器鏈)執(zhí)行到哪個(gè)過(guò)濾器
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
//獲取當(dāng)前指向的Filter的實(shí)例
filter = filterConfig.getFilter();
support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
filter, request, response);
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 {
//調(diào)用Filter的doFilter()方法
filter.doFilter(request, response, this);
}
這里的filter.doFilter(request, response, this);就是調(diào)用我們前面創(chuàng)建的TestFilter中的doFilter()方法。而TestFilter中的doFilter()方法會(huì)繼續(xù)調(diào)用chain.doFilter(request, response);方法,而這個(gè)chain其實(shí)就是ApplicationFilterChain,所以調(diào)用過(guò)程又回到了上面調(diào)用dofilter和調(diào)用internalDoFilter方法,這樣執(zhí)行直到里面的過(guò)濾器全部執(zhí)行。
如果定義兩個(gè)過(guò)濾器,則Debug結(jié)果如下:
相關(guān)文章
Java深入淺出掌握SpringBoot之MVC自動(dòng)配置原理篇
在進(jìn)行項(xiàng)目編寫(xiě)前,我們還需要知道一個(gè)東西,就是SpringBoot對(duì)我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們?cè)谥笫褂貌艜?huì)更加得心應(yīng)手2021-10-10使用jsoup解析html的table中的文本信息實(shí)例
今天小編就為大家分享一篇使用jsoup解析html的table中的文本信息實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05幾句話(huà)說(shuō)清session,cookie和token的區(qū)別及說(shuō)明
這篇文章主要介紹了幾句話(huà)說(shuō)清session,cookie和token的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12通過(guò)簡(jiǎn)易例子講解Java回調(diào)機(jī)制
這篇文章主要介紹了通過(guò)簡(jiǎn)易例子講解Java回調(diào)機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Java中的RASP機(jī)制實(shí)現(xiàn)詳解
這篇文章主要介紹了Java中的RASP實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08詳解elasticsearch實(shí)現(xiàn)基于拼音搜索
這篇文章主要為大家介紹了詳解elasticsearch實(shí)現(xiàn)基于拼音搜索示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01java實(shí)現(xiàn)乘地鐵方案的最優(yōu)選擇(票價(jià),距離)
這篇文章主要介紹了java實(shí)現(xiàn)乘地鐵方案的最優(yōu)選擇(票價(jià),距離),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07淺析Java中SimpleDateFormat為什么是線(xiàn)程不安全的
SimpleDateFormat是Java中用于日期時(shí)間格式化的一個(gè)類(lèi),它提供了對(duì)日期的解析和格式化能力,本文主要來(lái)和大家一起探討一下SimpleDateFormat為什么是線(xiàn)程不安全的,感興趣的可以了解下2024-02-02