JAVA設計形式之義務鏈形式詳解。本站提示廣大學習愛好者:(JAVA設計形式之義務鏈形式詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是JAVA設計形式之義務鏈形式詳解正文
在閻宏博士的《JAVA與形式》一書中開首是如許描寫義務鏈(Chain of Responsibility)形式的:
義務鏈形式是一種對象的行動形式。在義務鏈形式裡,許多對象由每個對象對其下家的援用而銜接起來構成一條鏈。要求在這個鏈上傳遞,直到鏈上的某一個對象決議處置此要求。收回這個要求的客戶端其實不曉得鏈上的哪個對象終究處置這個要求,這使得體系可以在不影響客戶真個情形下靜態地從新組織和分派義務。
從伐鼓傳花談起
伐鼓傳花是一種熱烈而又重要的喝酒游戲。在酒宴上賓客順次坐定地位,由一人伐鼓,伐鼓的處所與傳花的處所是離開的,以示公平。開端伐鼓時,花束就開端順次傳遞,鼓聲一落,假如花束在或人手中,則該人就得喝酒。
好比說,賈母、賈赦、賈政、賈寶玉和賈環是五個加入伐鼓傳花游戲的傳花者,他們構成一個環鏈。伐鼓者將花傳給賈母,開端傳花游戲。花由賈母傳給賈赦,由賈赦傳給賈政,由賈政傳給賈寶玉,又賈寶玉傳給賈環,由賈環傳回給賈母,如斯來去,以下圖所示。當鼓聲停滯時,手中有花的人就得履行酒令。
伐鼓傳花就是義務鏈形式的運用。義務鏈能夠是一條直線、一個環鏈或許一個樹構造的一部門。
義務鏈形式的構造
上面應用了一個義務鏈形式的最簡略的完成。
義務鏈形式觸及到的腳色以下所示:
● 籠統處置者(Handler)腳色:界說出一個處置要求的接口。假如須要,接口可以界說 出一個辦法以設定和前往對下家的援用。這個腳色平日由一個Java籠統類或許Java接話柄現。上圖中Handler類的聚合關系給出了詳細子類對下家的援用,籠統辦法handleRequest()標准了子類處置要求的操作。
● 詳細處置者(ConcreteHandler)腳色:詳細處置者接到要求後,可以選擇將要求處置失落,或許將要求傳給下家。因為詳細處置者持有對下家的援用,是以,假如須要,詳細處置者可以拜訪下家。
源代碼
籠統處置者腳色
public abstract class Handler {
/**
* 持有後繼的義務對象
*/
protected Handler successor;
/**
* 表示處置要求的辦法,固然這個表示辦法是沒有傳入參數的
* 但現實是可以傳入參數的,依據詳細須要來選擇能否傳遞參數
*/
public abstract void handleRequest();
/**
* 取值辦法
*/
public Handler getSuccessor() {
return successor;
}
/**
* 賦值辦法,設置後繼的義務對象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
}
詳細處置者腳色
public class ConcreteHandler extends Handler {
/**
* 處置辦法,挪用此辦法處置要求
*/
@Override
public void handleRequest() {
/**
* 斷定能否有後繼的義務對象
* 假如有,就轉發要求給後繼的義務對象
* 假如沒有,則處置要求
*/
if(getSuccessor() != null)
{
System.out.println("放過要求");
getSuccessor().handleRequest();
}else
{
System.out.println("處置要求");
}
}
}
客戶端類
public class Client {
public static void main(String[] args) {
//組裝義務鏈
Handler handler1 = new ConcreteHandler();
Handler handler2 = new ConcreteHandler();
handler1.setSuccessor(handler2);
//提交要求
handler1.handleRequest();
}
}
可以看出,客戶端創立了兩個處置者對象,並指定第一個處置者對象的下家是第二個處置者對象,而第二個處置者對象沒有下家。然後客戶端將要求傳遞給第一個處置者對象。
因為本示例的傳遞邏輯異常簡略:只需有下家,就傳給下家處置;假如沒有下家,就自行處置。是以,第一個處置者對象接到要求後,會將要求傳遞給第二個處置者對象。因為第二個處置者對象沒有下家,因而自行處置要求。運動時序圖以下所示。
應用場景
來斟酌如許一個功效:請求會餐費用的治理。
許多公司都是如許的福利,就是項目組或許是部分可以向公司請求一些會餐費用,用於組織項目構成員或許是部分成員停止會餐運動。
請求會餐費用的年夜致流程普通是:由請求人先填寫請求單,然後交給引導審批,假如請求同意上去,引導會告訴請求人審批經由過程,然後請求人去財政支付費用,假如沒有同意上去,引導會告訴請求人審批未經由過程,此事也就此作罷。
分歧級其余引導,關於審批的額度是紛歧樣的,好比,項目司理只能審批500元之內的請求;部分司理能審批1000元之內的請求;而總司理可以審核隨意率性額度的請求。
也就是說,當或人提出會餐費用請求的要求後,該要求會經過項目司理、部分司理、總司理當中的某一名引導來停止響應的處置,然則提出請求的人其實不曉得終究會由誰來處置他的要求,普通請求人是把本身的請求提交給項目司理,也許最初是由總司理來處置他的要求。
可使用義務鏈形式來完成上述功效:當或人提出會餐費用請求的要求後,該要求會在 項目司理—〉部分司理—〉總司理 如許一條引導處置鏈長進行傳遞,收回要求的人其實不曉得誰會來處置他的要求,每一個引導會依據本身的職責規模,來斷定是處置要求照樣把要求交給更高等其余引導,只需有引導處置了,傳遞就停止了。
須要把每位引導的處置自力出來,完成成零丁的職責處置對象,然後為它們供給一個公共的、籠統的父職責對象,如許便可以在客戶端來靜態地組合職責鏈,完成分歧的功效請求了。
源代碼
籠統處置者腳色類
public abstract class Handler {
/**
* 持有下一個處置要求的對象
*/
protected Handler successor = null;
/**
* 取值辦法
*/
public Handler getSuccessor() {
return successor;
}
/**
* 設置下一個處置要求的對象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
/**
* 處置會餐費用的請求
* @param user 請求人
* @param fee 請求的錢數
* @return 勝利或掉敗的詳細告訴
*/
public abstract String handleFeeRequest(String user , double fee);
}
詳細處置者腳色
public class ProjectManager extends Handler {
@Override
public String handleFeeRequest(String user, double fee) {
String str = "";
//項目司理權限比擬小,只能在500之內
if(fee < 500)
{
//為了測試,簡略點,只贊成張三的要求
if("張三".equals(user))
{
str = "勝利:項目司理贊成【" + user + "】的會餐費用,金額為" + fee + "元";
}else
{
//其別人一概分歧意
str = "掉敗:項目司理分歧意【" + user + "】的會餐費用,金額為" + fee + "元";
}
}else
{
//跨越500,持續傳遞給級別更高的人處置
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 = "";
//部分司理的權限只能在1000之內
if(fee < 1000)
{
//為了測試,簡略點,只贊成張三的要求
if("張三".equals(user))
{
str = "勝利:部分司理贊成【" + user + "】的會餐費用,金額為" + fee + "元";
}else
{
//其別人一概分歧意
str = "掉敗:部分司理分歧意【" + user + "】的會餐費用,金額為" + fee + "元";
}
}else
{
//跨越1000,持續傳遞給級別更高的人處置
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 = "";
//總司理的權限很年夜,只需要求到了這裡,他都可以處置
if(fee >= 1000)
{
//為了測試,簡略點,只贊成張三的要求
if("張三".equals(user))
{
str = "勝利:總司理贊成【" + user + "】的會餐費用,金額為" + fee + "元";
}else
{
//其別人一概分歧意
str = "掉敗:總司理分歧意【" + user + "】的會餐費用,金額為" + fee + "元";
}
}else
{
//假如還有後繼的處置對象,持續傳遞
if(getSuccessor() != null)
{
return getSuccessor().handleFeeRequest(user, fee);
}
}
return str;
}
}
客戶端類
public class Client {
public static void main(String[] args) {
//先要組裝義務鏈
Handler h1 = new GeneralManager();
Handler h2 = new DeptManager();
Handler h3 = new ProjectManager();
h3.setSuccessor(h2);
h2.setSuccessor(h1);
//開端測試
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);
}
}
運轉成果以下所示:
純的與不純的義務鏈形式
一個純的義務鏈形式請求一個詳細的處置者對象只能在兩個行動當選擇一個:一是承當義務,而是把義務推給下家。不許可湧現某一個詳細處置者對象在承當了一部門義務後又 把義務向下傳的情形。
在一個純的義務鏈形式外面,一個要求必需被某一個處置者對象所吸收;在一個不純的義務鏈形式外面,一個要求可以終究不被任何吸收端對象所吸收。
純的義務鏈形式的現實例子很難找到,普通看到的例子均是不純的義務鏈形式的完成。有些人以為不純的義務鏈基本不是義務鏈形式,這或許是有事理的。然則在現實的體系裡,純的義務鏈很難找到。假如保持義務鏈不純便不是義務鏈形式,那末義務鏈形式便不會有太年夜意義了。
義務鏈形式在Tomcat中的運用
盡人皆知Tomcat中的Filter就是應用了義務鏈形式,創立一個Filter除要在web.xml文件中做響應設置裝備擺設外,還須要完成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形式所看到的成果以下
其其實真正履行到TestFilter類之前,會經由許多Tomcat外部的類。順帶提一下其實Tomcat的容器設置也是義務鏈形式,留意被白色方框所圈中的類,從Engine到Host再到Context一向到Wrapper都是經由過程一個鏈傳遞要求。被綠色方框所圈中的處所有一個名為ApplicationFilterChain的類,ApplicationFilterChain類所飾演的就是籠統處置者腳色,而詳細處置者腳色由各個Filter飾演。
第一個疑問是ApplicationFilterChain將一切的Filter寄存在哪裡?
謎底是保留在ApplicationFilterChain類中的一個ApplicationFilterConfig對象的數組中。
/**
* Filters.
*/
private ApplicationFilterConfig[] filters =
new ApplicationFilterConfig[0];
那ApplicationFilterConfig對象又是甚麼呢?
ApplicationFilterConfig是一個Filter容器。以下是ApplicationFilterConfig類的聲明:
/**
* 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 $
*/
當一個web運用初次啟動時ApplicationFilterConfig會主動實例化,它會從該web運用的web.xml文件中讀取設置裝備擺設的Filter的信息,然後裝進該容器。
方才看到在ApplicationFilterChain類中所創立的ApplicationFilterConfig數組長度為零,那它是在甚麼時刻被從新賦值的呢?
private ApplicationFilterConfig[] filters =
new ApplicationFilterConfig[0];
是在挪用ApplicationFilterChain類的addFilter()辦法時。
/**
* 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用來記載以後過濾器鏈外面具有的過濾器數量,默許情形下n等於0,ApplicationFilterConfig對象數組的長度也等於0,所以當第一次挪用addFilter()辦法時,if (n == filters.length)的前提成立,ApplicationFilterConfig數組長度被轉變。以後filters[n++] = filterConfig;將變量filterConfig放入ApplicationFilterConfig數組中並將以後過濾器鏈外面具有的過濾器數量+1。
那ApplicationFilterChain的addFilter()辦法又是在甚麼處所被挪用的呢?
是在ApplicationFilterFactory類的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行以後為第二段。
第一段的重要目標是創立ApplicationFilterChain對象和一些參數設置。
第二段的重要目標是從高低文中獲得一切Filter信息,以後應用for輪回遍歷並挪用filterChain.addFilter(filterConfig);將filterConfig放入ApplicationFilterChain對象的ApplicationFilterConfig數組中。
那ApplicationFilterFactory類的createFilterChain()辦法又是在甚麼處所被挪用的呢?
是在StandardWrapperValue類的invoke()辦法中被挪用的。
因為invoke()辦法較長,所以將許多處所省略。
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());
...省略中央代碼
}
那正常的流程應當是如許的:
在StandardWrapperValue類的invoke()辦法中挪用ApplicationFilterChai類的createFilterChain()辦法———>在ApplicationFilterChai類的createFilterChain()辦法中挪用ApplicationFilterChain類的addFilter()辦法———>在ApplicationFilterChain類的addFilter()辦法中給ApplicationFilterConfig數組賦值。
依據下面的代碼可以看出StandardWrapperValue類的invoke()辦法在履行完createFilterChain()辦法後,會持續履行ApplicationFilterChain類的doFilter()辦法,然後在doFilter()辦法中會挪用internalDoFilter()辦法。
以下是internalDoFilter()辦法的部門代碼
// Call the next filter if there is one
if (pos < n) {
//拿到下一個Filter,將指針向下挪動一名
//pos它來標識以後ApplicationFilterChain(以後過濾器鏈)履行到哪一個過濾器
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
//獲得以後指向的Filter的實例
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 {
//挪用Filter的doFilter()辦法
filter.doFilter(request, response, this);
}
這裡的filter.doFilter(request, response, this);就是挪用我們後面創立的TestFilter中的doFilter()辦法。而TestFilter中的doFilter()辦法會持續挪用chain.doFilter(request, response);辦法,而這個chain其實就是ApplicationFilterChain,所以挪用進程又回到了下面挪用dofilter和挪用internalDoFilter辦法,如許履行直到外面的過濾器全體履行。
假如界說兩個過濾器,則Debug成果以下: