Struts2 源码分析——拦截器的机制
| 本章簡言 |
上一章講到關于action代理類的工作。即是如何去找對應的action配置信息,并執行action類的實例。而這一章筆者將講到在執行action需要用到的攔截器。為什么要講攔截器呢?可以這樣子講吧。攔截器的應用是sturts2核心的亮點之一。如果不明白攔截器是什么的話,那么你相當于沒有學習過struts2。筆者本來想直接講這一章的知識點。可是又怕讀者可能對攔截器沒有一個概念化的理解。為什么這么講呢?struts2在設計攔截器這一個部分的內容。在筆者看來事實是以AOP為核心思想來設計的。所以就是必須先理解一下AOP思想到底是什么東東。只有這樣子才能更好的去理解struts2的攔截器。
| AOP思想 |
?AOP思想的全名為Aspect Oriented Programming。即是面向切面編程。相信讀者者聽過OOP(Object-Oriented Programing,面向對象編程)。筆者也認AOP是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。為什么筆者這邊用“也”這個字呢?筆者不是一個喜歡吹牛的人。會就是會,不會就是不會。網絡上有很多關于AOP思想的資料,筆者就是通過這些資料學習的,也認同AOP是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。所以筆者這里也只是簡章的講解一下AOP思想。希望讀者見諒。
什么叫做面向切面編程呢?如果用專業的角度來講切面叫作Aspect。struts2攔截器相關的類就是切面(Aspect)。完了。什么東東啊!沒事。筆者就土話來講吧。對于傳統面向對象編程來講,執行業務代碼一般都會在方法里面。這一點大家都知道。相信大家也知道在執行相關業務代碼之前也會執行對應的驗證代碼。作為一個軟件設計師在設計整個構架的時候,一定會理解業務并把業務劃分為幾個獨立的模塊。而每模塊都一定會有相關的驗證代碼。如什么數據不能為空等相關驗證代碼。甚至有一些軟件系統希望有對應的日志跟蹤的時候,對應的日志代碼也要寫入進去。如圖下。
可以說圖上是從縱向角度來看。把業務劃分出多個獨立業務模塊,每一個業務模塊都有相應的數據驗證和日志記錄。筆者認為AOP思想則是要橫向角度來看。什么說呢?筆者在上面的圖片加入橫向角度之后會是一個什么樣子。如圖下
當筆者把橫向角度加入之后,就是會發現把業務模塊相關代碼和其他代碼進行了分離獨立起來了。如上面圖片中的圖1就是加入之后的狀態了。那么筆者是什么知道要這樣子分呢?主要是關注點。即是橫向關注點。筆者希望把業務代碼和其他代碼進行分離獨立起來。這就是筆者的關注點(這里的業務代碼是總業務代碼。即是把所有的業務代碼看作一個整體)。而圖片中的圖2便是最后的結果。為什么三塊驗證代碼最后變成一個塊呢?舉個簡單的例子吧。筆者相信有工作經驗的程序員都會經驗過非空的代碼或是非空并沒有特殊的字符等相關的驗證吧。難道你不覺得這塊的代碼邏輯是可以共通的嗎?當然筆者也想過這樣子的問題——新一個專門用來實現驗證功能的類不就行了嗎?沒有錯。是可以的。可是AOP思想的核心筆者認為不是在這里。他的目標是讓業務模塊去選擇自己對應的驗證代碼。驗證代碼就是切面(Aspect)。什么意思呢?當業務模塊相關代碼和其他代碼進行了分離獨立的時候。其他代碼這個部分事實上是可以進行重組和切分。比如:日志相關的代碼變成一個日志切面(只是一個類)。性能相關代碼變成一個性能切面。各個切面之間是相互獨立的。最后就是變成了根據不同的業務模塊去選擇不同需要的切面。圖片上的圖2就是最后筆者得出來的結果。當然筆者也不敢說自己是對的。個人看法而以。
對于上面AOP理解也是筆者自己的個人看法而以。筆者也不敢說是對的。每一個人的理解是不一樣子的。也不一定見得讀者的理解是錯的。顯然筆者認為AOP真的很不錯。縱向把業務劃分出模塊。橫向把代碼劃出模塊。
| ?攔截器的執行機制 |
struts2的攔截器筆者認為就是AOP思想的一種體現。在進入action類實例之前必須先執行相關攔截器。即是攔截器相當于AOP思想里面的切面。把用戶action類和攔截器分離獨立,就像筆者上面講的橫向關注點一樣子。因為大部分的用戶action類是跟業務有關系的。所以strust2里面有很多攔截器。不同的action類可能會選擇不同的攔截器。當然也有一些默認必須有的攔截器。從《Struts2 源碼分析——Action代理類的工作》章節里面我們知道執行action請求是在DefaultActionInvocation類的invoke方法。可以這樣子講吧。一切都從這個方法開始的。如下
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()) {//獲得一個攔截器 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);//執行攔截器 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 }上面的紅色的代碼是這個方法的核心點之一。也是實現AOP思想的代碼亮點。讓我們看一下紅色代碼做什么?判斷interceptors是否有攔截器。如果沒有就直接執行invokeActionOnly方法。即是執行action類實例對應的方法。如果有就獲得攔截器并執行攔截器(執行intercept方法)。好了。關鍵點就在這個執行攔截器身上。即是執行intercept方法。intercept方法有一個參數就是DefaultActionInvocation類的接口。這個參數讓struts2的AOP思想能夠進行。為什么這樣子講呢?不清楚讀者有沒有想過。為什么這邊判斷攔截器是用if而不是用for 或是 while呢?必竟攔截器不只一個。我們都清楚AOP的目標就是讓業務模塊選擇對應的切面。那么就有可能存在多個攔截器。這也是為什么亮點的原因了。看一下攔截器的代碼就知道了。如下
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 }上面的源碼是筆者從多個攔截器中選擇一個比較簡單的來看。不清楚你們明白了沒有。紅色的代碼已經很明確說明了一件事情。攔截器開始的時候,執行相關的攔截器邏輯,然后又重新調用DefaultActionInvocation類的invoke方法。從而獲得下一個攔截器。就是這樣子下一個攔截器又開始執行自己的intercept方法。做了相關的攔截器邏輯之后。又一次重新調用DefaultActionInvocation類的invoke方法。又做了相似的工作。只到沒有了攔截器,執行用戶action類實例的方法并返回結果。有了結果之后,就開始續繼執行當前上一個攔截器的后半部分代碼。只到返回到最開始的攔截器執行后半部分的代碼。如果硬要說一個相似的專業詞語的話。筆者會想到方法疊帶。
筆者心里面對這一部分的做法一直很喜歡。當然也不少人不會認同筆者的觀念。可以看得出來AOP思想把個個切面和業務模塊處理的非常好。切面和業務模塊又是獨立的互不影響。同時可以讓開發人員更加關注對應的業務邏輯。?
注意:學習這一部分最好結合《Struts2 源碼分析——核心機制》的核心機制的圖片。這樣子會更好理解。
| 本章總結 |
本章主要是講到關于攔截器的運行機制。知道了struts2是如果進行處理攔截器和action類實例之間關系。同時也了解了相關AOP思想。
轉載于:https://www.cnblogs.com/hayasi/p/5882397.html
總結
以上是生活随笔為你收集整理的Struts2 源码分析——拦截器的机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 子选择器与后代选择器的区别
- 下一篇: Bootstrap入门(二十一)组件15