Struts2 源码分析——拦截器的机制
| 本章簡(jiǎn)言 |
上一章講到關(guān)于action代理類的工作。即是如何去找對(duì)應(yīng)的action配置信息,并執(zhí)行action類的實(shí)例。而這一章筆者將講到在執(zhí)行action需要用到的攔截器。為什么要講攔截器呢?可以這樣子講吧。攔截器的應(yīng)用是sturts2核心的亮點(diǎn)之一。如果不明白攔截器是什么的話,那么你相當(dāng)于沒(méi)有學(xué)習(xí)過(guò)struts2。筆者本來(lái)想直接講這一章的知識(shí)點(diǎn)??墒怯峙伦x者可能對(duì)攔截器沒(méi)有一個(gè)概念化的理解。為什么這么講呢?struts2在設(shè)計(jì)攔截器這一個(gè)部分的內(nèi)容。在筆者看來(lái)事實(shí)是以AOP為核心思想來(lái)設(shè)計(jì)的。所以就是必須先理解一下AOP思想到底是什么東東。只有這樣子才能更好的去理解struts2的攔截器。
| AOP思想 |
?AOP思想的全名為Aspect Oriented Programming。即是面向切面編程。相信讀者者聽(tīng)過(guò)OOP(Object-Oriented Programing,面向?qū)ο缶幊?。筆者也認(rèn)AOP是OOP(Object-Oriented Programing,面向?qū)ο缶幊?#xff09;的補(bǔ)充和完善。為什么筆者這邊用“也”這個(gè)字呢?筆者不是一個(gè)喜歡吹牛的人。會(huì)就是會(huì),不會(huì)就是不會(huì)。網(wǎng)絡(luò)上有很多關(guān)于AOP思想的資料,筆者就是通過(guò)這些資料學(xué)習(xí)的,也認(rèn)同AOP是OOP(Object-Oriented Programing,面向?qū)ο缶幊?#xff09;的補(bǔ)充和完善。所以筆者這里也只是簡(jiǎn)章的講解一下AOP思想。希望讀者見(jiàn)諒。
什么叫做面向切面編程呢?如果用專業(yè)的角度來(lái)講切面叫作Aspect。struts2攔截器相關(guān)的類就是切面(Aspect)。完了。什么東東啊!沒(méi)事。筆者就土話來(lái)講吧。對(duì)于傳統(tǒng)面向?qū)ο缶幊虂?lái)講,執(zhí)行業(yè)務(wù)代碼一般都會(huì)在方法里面。這一點(diǎn)大家都知道。相信大家也知道在執(zhí)行相關(guān)業(yè)務(wù)代碼之前也會(huì)執(zhí)行對(duì)應(yīng)的驗(yàn)證代碼。作為一個(gè)軟件設(shè)計(jì)師在設(shè)計(jì)整個(gè)構(gòu)架的時(shí)候,一定會(huì)理解業(yè)務(wù)并把業(yè)務(wù)劃分為幾個(gè)獨(dú)立的模塊。而每模塊都一定會(huì)有相關(guān)的驗(yàn)證代碼。如什么數(shù)據(jù)不能為空等相關(guān)驗(yàn)證代碼。甚至有一些軟件系統(tǒng)希望有對(duì)應(yīng)的日志跟蹤的時(shí)候,對(duì)應(yīng)的日志代碼也要寫(xiě)入進(jìn)去。如圖下。
可以說(shuō)圖上是從縱向角度來(lái)看。把業(yè)務(wù)劃分出多個(gè)獨(dú)立業(yè)務(wù)模塊,每一個(gè)業(yè)務(wù)模塊都有相應(yīng)的數(shù)據(jù)驗(yàn)證和日志記錄。筆者認(rèn)為AOP思想則是要橫向角度來(lái)看。什么說(shuō)呢?筆者在上面的圖片加入橫向角度之后會(huì)是一個(gè)什么樣子。如圖下
當(dāng)筆者把橫向角度加入之后,就是會(huì)發(fā)現(xiàn)把業(yè)務(wù)模塊相關(guān)代碼和其他代碼進(jìn)行了分離獨(dú)立起來(lái)了。如上面圖片中的圖1就是加入之后的狀態(tài)了。那么筆者是什么知道要這樣子分呢?主要是關(guān)注點(diǎn)。即是橫向關(guān)注點(diǎn)。筆者希望把業(yè)務(wù)代碼和其他代碼進(jìn)行分離獨(dú)立起來(lái)。這就是筆者的關(guān)注點(diǎn)(這里的業(yè)務(wù)代碼是總業(yè)務(wù)代碼。即是把所有的業(yè)務(wù)代碼看作一個(gè)整體)。而圖片中的圖2便是最后的結(jié)果。為什么三塊驗(yàn)證代碼最后變成一個(gè)塊呢?舉個(gè)簡(jiǎn)單的例子吧。筆者相信有工作經(jīng)驗(yàn)的程序員都會(huì)經(jīng)驗(yàn)過(guò)非空的代碼或是非空并沒(méi)有特殊的字符等相關(guān)的驗(yàn)證吧。難道你不覺(jué)得這塊的代碼邏輯是可以共通的嗎?當(dāng)然筆者也想過(guò)這樣子的問(wèn)題——新一個(gè)專門用來(lái)實(shí)現(xiàn)驗(yàn)證功能的類不就行了嗎?沒(méi)有錯(cuò)。是可以的??墒茿OP思想的核心筆者認(rèn)為不是在這里。他的目標(biāo)是讓業(yè)務(wù)模塊去選擇自己對(duì)應(yīng)的驗(yàn)證代碼。驗(yàn)證代碼就是切面(Aspect)。什么意思呢?當(dāng)業(yè)務(wù)模塊相關(guān)代碼和其他代碼進(jìn)行了分離獨(dú)立的時(shí)候。其他代碼這個(gè)部分事實(shí)上是可以進(jìn)行重組和切分。比如:日志相關(guān)的代碼變成一個(gè)日志切面(只是一個(gè)類)。性能相關(guān)代碼變成一個(gè)性能切面。各個(gè)切面之間是相互獨(dú)立的。最后就是變成了根據(jù)不同的業(yè)務(wù)模塊去選擇不同需要的切面。圖片上的圖2就是最后筆者得出來(lái)的結(jié)果。當(dāng)然筆者也不敢說(shuō)自己是對(duì)的。個(gè)人看法而以。
對(duì)于上面AOP理解也是筆者自己的個(gè)人看法而以。筆者也不敢說(shuō)是對(duì)的。每一個(gè)人的理解是不一樣子的。也不一定見(jiàn)得讀者的理解是錯(cuò)的。顯然筆者認(rèn)為AOP真的很不錯(cuò)。縱向把業(yè)務(wù)劃分出模塊。橫向把代碼劃出模塊。
| ?攔截器的執(zhí)行機(jī)制 |
struts2的攔截器筆者認(rèn)為就是AOP思想的一種體現(xiàn)。在進(jìn)入action類實(shí)例之前必須先執(zhí)行相關(guān)攔截器。即是攔截器相當(dāng)于AOP思想里面的切面。把用戶action類和攔截器分離獨(dú)立,就像筆者上面講的橫向關(guān)注點(diǎn)一樣子。因?yàn)榇蟛糠值挠脩鬭ction類是跟業(yè)務(wù)有關(guān)系的。所以strust2里面有很多攔截器。不同的action類可能會(huì)選擇不同的攔截器。當(dāng)然也有一些默認(rèn)必須有的攔截器。從《Struts2 源碼分析——Action代理類的工作》章節(jié)里面我們知道執(zhí)行action請(qǐng)求是在DefaultActionInvocation類的invoke方法??梢赃@樣子講吧。一切都從這個(gè)方法開(kāi)始的。如下
DefaultActionInvocation類:
1 public String invoke() throws Exception { 2 String profileKey = "invoke: "; 3 try { 4 UtilTimerStack.push(profileKey); 5 6 if (executed) { 7 throw new IllegalStateException("Action has already executed"); 8 } 9 10 if (interceptors.hasNext()) {//獲得一個(gè)攔截器 11 final InterceptorMapping interceptor = interceptors.next(); 12 String interceptorMsg = "interceptor: " + interceptor.getName(); 13 UtilTimerStack.push(interceptorMsg); 14 try { 15 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);//執(zhí)行攔截器 16 } finally { 17 UtilTimerStack.pop(interceptorMsg); 18 } 19 } else { 20 resultCode = invokeActionOnly(); 21 } 22 23 // this is needed because the result will be executed, then control will return to the Interceptor, which will 24 // return above and flow through again 25 if (!executed) { 26 if (preResultListeners != null) { 27 LOG.trace("Executing PreResultListeners for result [{}]", result); 28 29 for (Object preResultListener : preResultListeners) { 30 PreResultListener listener = (PreResultListener) preResultListener; 31 32 String _profileKey = "preResultListener: "; 33 try { 34 UtilTimerStack.push(_profileKey); 35 listener.beforeResult(this, resultCode); 36 } 37 finally { 38 UtilTimerStack.pop(_profileKey); 39 } 40 } 41 } 42 43 // now execute the result, if we're supposed to 44 if (proxy.getExecuteResult()) { 45 executeResult(); 46 } 47 48 executed = true; 49 } 50 51 return resultCode; 52 } 53 finally { 54 UtilTimerStack.pop(profileKey); 55 } 56 }上面的紅色的代碼是這個(gè)方法的核心點(diǎn)之一。也是實(shí)現(xiàn)AOP思想的代碼亮點(diǎn)。讓我們看一下紅色代碼做什么?判斷interceptors是否有攔截器。如果沒(méi)有就直接執(zhí)行invokeActionOnly方法。即是執(zhí)行action類實(shí)例對(duì)應(yīng)的方法。如果有就獲得攔截器并執(zhí)行攔截器(執(zhí)行intercept方法)。好了。關(guān)鍵點(diǎn)就在這個(gè)執(zhí)行攔截器身上。即是執(zhí)行intercept方法。intercept方法有一個(gè)參數(shù)就是DefaultActionInvocation類的接口。這個(gè)參數(shù)讓struts2的AOP思想能夠進(jìn)行。為什么這樣子講呢?不清楚讀者有沒(méi)有想過(guò)。為什么這邊判斷攔截器是用if而不是用for 或是 while呢?必竟攔截器不只一個(gè)。我們都清楚AOP的目標(biāo)就是讓業(yè)務(wù)模塊選擇對(duì)應(yīng)的切面。那么就有可能存在多個(gè)攔截器。這也是為什么亮點(diǎn)的原因了??匆幌聰r截器的代碼就知道了。如下
LoggingInterceptor類:
1 public String intercept(ActionInvocation invocation) throws Exception { 2 logMessage(invocation, START_MESSAGE); 3 String result = invocation.invoke(); 4 logMessage(invocation, FINISH_MESSAGE); 5 return result; 6 }上面的源碼是筆者從多個(gè)攔截器中選擇一個(gè)比較簡(jiǎn)單的來(lái)看。不清楚你們明白了沒(méi)有。紅色的代碼已經(jīng)很明確說(shuō)明了一件事情。攔截器開(kāi)始的時(shí)候,執(zhí)行相關(guān)的攔截器邏輯,然后又重新調(diào)用DefaultActionInvocation類的invoke方法。從而獲得下一個(gè)攔截器。就是這樣子下一個(gè)攔截器又開(kāi)始執(zhí)行自己的intercept方法。做了相關(guān)的攔截器邏輯之后。又一次重新調(diào)用DefaultActionInvocation類的invoke方法。又做了相似的工作。只到?jīng)]有了攔截器,執(zhí)行用戶action類實(shí)例的方法并返回結(jié)果。有了結(jié)果之后,就開(kāi)始續(xù)繼執(zhí)行當(dāng)前上一個(gè)攔截器的后半部分代碼。只到返回到最開(kāi)始的攔截器執(zhí)行后半部分的代碼。如果硬要說(shuō)一個(gè)相似的專業(yè)詞語(yǔ)的話。筆者會(huì)想到方法疊帶。
筆者心里面對(duì)這一部分的做法一直很喜歡。當(dāng)然也不少人不會(huì)認(rèn)同筆者的觀念。可以看得出來(lái)AOP思想把個(gè)個(gè)切面和業(yè)務(wù)模塊處理的非常好。切面和業(yè)務(wù)模塊又是獨(dú)立的互不影響。同時(shí)可以讓開(kāi)發(fā)人員更加關(guān)注對(duì)應(yīng)的業(yè)務(wù)邏輯。?
注意:學(xué)習(xí)這一部分最好結(jié)合《Struts2 源碼分析——核心機(jī)制》的核心機(jī)制的圖片。這樣子會(huì)更好理解。
| 本章總結(jié) |
本章主要是講到關(guān)于攔截器的運(yùn)行機(jī)制。知道了struts2是如果進(jìn)行處理攔截器和action類實(shí)例之間關(guān)系。同時(shí)也了解了相關(guān)AOP思想。
轉(zhuǎn)載于:https://www.cnblogs.com/hayasi/p/5882397.html
總結(jié)
以上是生活随笔為你收集整理的Struts2 源码分析——拦截器的机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 子选择器与后代选择器的区别
- 下一篇: Bootstrap入门(二十一)组件15