SpringMVC原始碼剖析(一)- 從抽象和介面說起
SpringMVC作為Struts2之後異軍突起的一個表現層框架,正越來越流行,相信javaee的開發者們就算沒使用過SpringMVC,也應該對其略有耳聞。我試圖通過對SpringMVC的設計思想和原始碼實現的剖析,從抽象意義上的設計層面和實現意義上的程式碼層面兩個方面,逐一揭開SpringMVC神祕的面紗,本文的程式碼,都是基於Spring的 3.1.3RELEASE版本。
任何一個框架,都有自己特定的適用領域,框架的設計和實現,必定是為了應付該領域內許多通用的,煩瑣的、基礎的工作而生。SpringMVC作為一個表現層框架,也必須直面Web開發領域中表現層中的幾大課題,並給出自己的回答:
- URL到框架的對映。
- http請求引數繫結
- http響應的生成和輸出
這三大課題,組成一個完整的web請求流程,每一個部分都具有非常廣闊的外延。SpringMVC框架對這些課題的回答又是什麼呢?
學習一個框架,首要的是要先領會它的設計思想。從抽象、從全域性上來審視這個框架。其中最具有參考價值的,就是這個框架所定義的核心介面。核心介面定義了框架的骨架,也在最抽象的意義上表達了框架的設計思想。
下面我以一個web請求流程為載體,依次介紹SpringMVC的核心介面和類。
使用者在瀏覽器中,輸入了http://www.xxxx.com/aaa/bbb.ccc的地址,回車後,瀏覽器發起一個http請求。請求到達你的伺服器後,首先會被SpringMVC註冊在web.xml中的前端轉發器DispatcherServlet接收,DispatcherServlet是一個標準的Servlet,它的作用是接受和轉發web請求到內部框架處理單元。
下面看一下第一個出現在你面前的核心介面,它是在org.springframework.web.servlet包中定義的HandlerMapping介面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package
org.springframework.web.servlet; import
javax.servlet.http.HttpServletRequest; public
interface
HandlerMapping { String
PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping. class .getName()
+ ".pathWithinHandlerMapping" ; String
BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping. class .getName()
+ ".bestMatchingPattern" ; String
INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping. class .getName()
+ ".introspectTypeLevelMapping" ; String
URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping. class .getName()
+ ".uriTemplateVariables" ; String
PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping. class .getName()
+ ".producibleMediaTypes" ; HandlerExecutionChain
getHandler(HttpServletRequest request) throws
Exception; } |
為了閱讀方便,我去掉了原始碼中的註釋,但是我強烈建議你一定要記得去閱讀它,這樣你才能從框架的設計者口中得到最準確的關於這個類或者介面的設計說明。類中定義的幾個常量,我們先不去管它。關鍵在於這個介面中唯一的方法:
1
|
HandlerExecutionChain
getHandler(HttpServletRequest request) throws
Exception; |
這個方法就算對於一個java初學者來說,也很容易理解:它只有一個型別為HttpServletRequest的引數,throws Exception的宣告表示它不處理任何型別的異常,HandlerExecutionChain是它的返回型別。
回到DispatcherServlet的處理流程,當DispatcherServlet接收到web請求後,由標準Servlet類處理方法doGet或者doPost,經過幾次轉發後,最終註冊在DispatcherServlet類中的HandlerMapping實現類組成的一個List(有點拗口)會在一個迴圈中被遍歷。以該web請求的HttpServletRequest物件為引數,依次呼叫其getHandler方法,第一個不為null的呼叫結果,將被返回。DispatcherServlet類中的這個遍歷方法不長,貼一下,讓大家有更直觀的瞭解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/** *
Return the HandlerExecutionChain for this request. *
<p>Tries all handler mappings in order. *
@param request current HTTP request *
@return the HandlerExecutionChain, or <code>null</code> if no handler could be found */ protected
HandlerExecutionChain getHandler(HttpServletRequest request) throws
Exception { for
(HandlerMapping hm : this .handlerMappings)
{ if
(logger.isTraceEnabled()) { logger.trace( "Testing
handler map ["
+ hm + "]
in DispatcherServlet with name '"
+ getServletName() + "'" ); } HandlerExecutionChain
handler = hm.getHandler(request); if
(handler != null )
{ return
handler; } } return
null ; } |
是的,第一步處理就這麼簡單的完成了。一個web請求經過處理後,會得到一個HandlerExecutionChain物件,這就是SpringMVC對URl對映給出的回答。需要留意的是,HandlerMapping介面的getHandler方法引數是HttpServletRequest,這意味著,HandlerMapping的實現類可以利用HttpServletRequest中的 所有資訊來做出這個HandlerExecutionChain物件的生成”決策“。這包括,請求頭、url路徑、cookie、session、引數等等一切你從一個web請求中可以得到的任何東西(最常用的是url路徑)。
SpirngMVC的第一個擴充套件點,就出現在這裡。我們可以編寫任意的HandlerMapping實現類,依據任何策略來決定一個web請求到HandlerExecutionChain物件的生成。可以說,從第一個核心介面的宣告開始,SpringMVC就把自己的靈活性和野心暴露無疑:哥玩的就是”Open-Closed“。
HandlerExecutionChain這個類,就是我們下一個要了解的核心類。從名字可以直觀的看得出,這個物件是一個執行鏈的封裝。熟悉Struts2的都知道,Action物件也是被層層攔截器包裝,這裡可以做個類比,說明SpringMVC確實是吸收了Struts2的部分設計思想。
HandlerExecutionChain類的程式碼不長,它定義在org.springframework.web.servlet包中,為了更直觀的理解,先上程式碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
package
org.springframework.web.servlet; import
java.util.ArrayList; import
java.util.Arrays; import
java.util.List; import
org.springframework.util.CollectionUtils; public
class
HandlerExecutionChain { private
final
Object handler; private
HandlerInterceptor[] interceptors; private
List<HandlerInterceptor> interceptorList; public
HandlerExecutionChain(Object handler) { this (handler,
null ); } public
HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) { if
(handler instanceof
HandlerExecutionChain) { HandlerExecutionChain
originalChain = (HandlerExecutionChain) handler; this .handler
= originalChain.getHandler(); this .interceptorList
= new
ArrayList<HandlerInterceptor>(); CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(),
this .interceptorList); CollectionUtils.mergeArrayIntoCollection(interceptors,
this .interceptorList); } else
{ this .handler
= handler; this .interceptors
= interceptors; } } public
Object getHandler() { return
this .handler; } public
void
addInterceptor(HandlerInterceptor interceptor) { initInterceptorList(); this .interceptorList.add(interceptor); } public
void
addInterceptors(HandlerInterceptor[] interceptors) { if
(interceptors != null )
{ initInterceptorList(); this .interceptorList.addAll(Arrays.asList(interceptors)); } } private
void
initInterceptorList() { if
( this .interceptorList
== null )
{ this .interceptorList
= new
ArrayList<HandlerInterceptor>(); } if
( this .interceptors
!= null )
{ this .interceptorList.addAll(Arrays.asList( this .interceptors)); this .interceptors
= null ; } } public
HandlerInterceptor[] getInterceptors() { if
( this .interceptors
== null
&& this .interceptorList
!= null )
{ this .interceptors
= this .interceptorList.toArray( new
HandlerInterceptor[ this .interceptorList.size()]); } return
this .interceptors; } @Override public
String toString() { if
( this .handler
== null )
{ return
"HandlerExecutionChain with no handler" ; } StringBuilder
sb = new
StringBuilder(); sb.append( "HandlerExecutionChain
with handler [" ).append( this .handler).append( "]" ); if
(!CollectionUtils.isEmpty( this .interceptorList))
{ sb.append( "
and " ).append( this .interceptorList.size()).append( "
interceptor" ); if
( this .interceptorList.size()
> 1 )
{ sb.append( "s" ); } } return
sb.toString(); } } |
1
2
3
|
private
final
Object handler; private
HandlerInterceptor[] interceptors; |
不出我們所料,一個實質執行物件,還有一堆攔截器。這不就是Struts2中的實現麼,SpringMVC沒有避嫌,還是採用了這種封裝。得到HandlerExecutionChain這個執行鏈(execution chain)之後,下一步的處理將圍繞其展開。
HandlerInterceptor也是SpringMVC的核心介面,定義如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package
org.springframework.web.servlet; import
javax.servlet.http.HttpServletRequest; import
javax.servlet.http.HttpServletResponse; public
interface
HandlerInterceptor { boolean
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception; void
postHandle( HttpServletRequest
request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws
Exception; void
afterCompletion( HttpServletRequest
request, HttpServletResponse response, Object handler, Exception ex) throws
Exception; } |
至此,HandlerExecutionChain整個執行脈絡也就清楚了:在真正呼叫其handler物件前,HandlerInterceptor介面實現類組成的陣列將會被遍歷,其preHandle方法會被依次呼叫,然後真正的handler物件將被呼叫。
handler物件被呼叫後,就生成了需要的響應資料,在將處理結果寫到HttpServletResponse物件之前(SpringMVC稱為渲染檢視),其postHandle方法會被依次呼叫。檢視渲染完成後,最後afterCompletion方法會被依次呼叫,整個web請求的處理過程就結束了。
在一個處理物件執行之前,之後利用攔截器做文章,這已經成為一種經典的框架設計套路。Struts2中的攔截器會做諸如引數繫結這類複雜的工作,那麼SpringMVC的攔截器具體做些什麼呢?我們暫且不關心,雖然這是很重要的細節,但細節畢竟是細節,我們先來理解更重要的東西。
HandlerInterceptor,是SpringMVC的第二個擴充套件點的暴露,通過自定義攔截器,我們可以在一個請求被真正處理之前、請求被處理但還沒輸出到響應中、請求已經被輸出到響應中之後這三個時間點去做任何我們想要做的事情。Struts2框架的成功,就是源於這種攔截器的設計,SpringMVC吸收了這種設計思想,並推陳出新,更合理的劃分了三個不同的時間點,從而給web請求處理這個流程,提供了更大的擴充套件性。
這個HandlerExecutionChain類中以Object引用所宣告的handler物件,到底是個什麼東東?它是怎麼被呼叫的?
回答這些問題之前,先看SpringMVC中的又一個核心介面,HandlerAdapter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package
org.springframework.web.servlet; import
javax.servlet.http.HttpServletRequest; import
javax.servlet.http.HttpServletResponse; public
interface
HandlerAdapter { boolean
supports(Object handler); ModelAndView
handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
Exception; long
getLastModified(HttpServletRequest request, Object handler); } |
1
2
3
4
5
|
/**
List of HandlerMappings used by this servlet */ private
List<HandlerMapping> handlerMappings; /**
List of HandlerAdapters used by this servlet */ private
List<HandlerAdapter> handlerAdapters; |
接下來,我們再以DispatcherServlet類中另外一段程式碼來回答上述的問題:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/** *
Return the HandlerAdapter for this handler object. *
@param handler the handler object to find an adapter for *
@throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error. */ protected
HandlerAdapter getHandlerAdapter(Object handler) throws
ServletException { for
(HandlerAdapter ha : this .handlerAdapters)
{ if
(logger.isTraceEnabled()) { logger.trace( "Testing
handler adapter ["
+ ha + "]" ); } if
(ha.supports(handler)) { return
ha; } } throw
new
ServletException( "No
adapter for handler ["
+ handler + "]:
Does your handler implement a supported interface like Controller?" ); } |
ModelAndView物件的程式碼就不貼了,它是SpringMVC中對檢視和資料的一個聚合類。其中的檢視,就是由SpringMVC的最後一個核心介面View所抽象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package
org.springframework.web.servlet; import
java.util.Map; import
javax.servlet.http.HttpServletRequest; import
javax.servlet.http.HttpServletResponse; public
interface
View { String
RESPONSE_STATUS_ATTRIBUTE = View. class .getName()
+ ".responseStatus" ; String
PATH_VARIABLES = View. class .getName()
+ ".pathVariables" ; String
getContentType(); void
render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws
Exception; } |
所有的資料,最後會作為一個Map物件傳遞到View實現類中的render方法,呼叫這個render方法,就完成了檢視到響應的渲染。這個View實現類,就是來自HandlerAdapter中的handle方法的返回結果。當然從ModelAndView到真正的View實現類有一個解析的過程,ModelAndView中可以有真正的檢視物件,也可以只是有一個檢視的名字,SpringMVC會負責將檢視名稱解析為真正的檢視物件。
至此,我們瞭解了一個典型的完整的web請求在SpringMVC中的處理過程和其中涉及到的核心類和介面。
在一個典型的SpringMVC呼叫中,HandlerExecutionChain中封裝handler物件就是用@Controller註解標識的類的一個例項,根據類級別和方法級別的@RequestMapping註解,由預設註冊的DefaultAnnotationHandlerMapping(3.1.3中更新為RequestMappingHandlerMapping類,但是為了向後相容,DefaultAnnotationHandlerMapping也可以使用)生成HandlerExecutionChain物件,再由AnnotationMethodHandlerAdapter(3.1.3中更新為RequestMappingHandlerAdapter類,但是為了向後相容,AnnotationMethodHandlerAdapter也可以使用)來執行這個HandlerExecutionChain物件,生成最終的ModelAndView物件後,再由具體的View物件的render方法渲染檢視。
可以看到,作為一個表現層框架,SpringMVC沒有像Struts2那樣激進,並沒有採用和Web容器完全解耦的設計思想,而是以原生的Servlet框架物件為依託,通過合理的抽象,制定了嚴謹的的處理流程。這樣做的結果是,執行效率比Struts2要高,靈活性也上升了一個層次。
轉載:http://my.oschina.net/lichhao/blog/99039
相關文章
- 從原始碼角度解析執行緒池中頂層介面和抽象類原始碼執行緒抽象
- JUnit原始碼分析(四)——從Decorator模式說起原始碼模式
- Koa原始碼閱讀(一)從搭建Web伺服器說起原始碼Web伺服器
- SpringMVC原始碼剖析(二)- DispatcherServlet的前世今生SpringMVC原始碼Servlet
- 《STL原始碼剖析》-- 特別說明原始碼
- slab原始碼分析--從slab初始化說起原始碼
- 跟我一起剖析 Java 併發原始碼之 UnsafeJava原始碼
- 從程式碼生成說起,帶你深入理解 mybatis generator 原始碼MyBatis原始碼
- Flutter 原始碼剖析(一)Flutter原始碼
- vue原始碼剖析(一)Vue原始碼
- Kafka 原始碼剖析(一)Kafka原始碼
- SpringMVC原始碼剖析(三)- DispatcherServlet的初始化流SpringMVC原始碼Servlet
- SpringMVC原始碼剖析(三)- DispatcherServlet的初始化流程SpringMVC原始碼Servlet
- zanphp原始碼解讀 – MVC說起PHP原始碼MVC
- Redis原始碼剖析之主從複製Redis原始碼
- 從一個埋點日誌上報指令碼說起指令碼
- 介面和抽象理解抽象
- 介面和抽象類抽象
- 抽象類和介面抽象
- 從SpringMvc原始碼分析其工作原理SpringMVC原始碼
- Java集合原始碼剖析——ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】Vector原始碼剖析Java原始碼
- 【Java集合原始碼剖析】HashMap原始碼剖析Java原始碼HashMap
- 【Java集合原始碼剖析】Hashtable原始碼剖析Java原始碼
- 【Java集合原始碼剖析】TreeMap原始碼剖析Java原始碼
- ArrayList 從原始碼角度剖析底層原理原始碼
- 說說SpringMVC從http流到Controller介面引數的轉換過程SpringMVCHTTPController
- SpringMVC原始碼剖析(五)-訊息轉換器HttpMessageConverterSpringMVC原始碼HTTP
- SpringMVC原始碼剖析(四)- DispatcherServlet請求轉發的實現SpringMVC原始碼Servlet
- 【Java集合原始碼剖析】LinkedList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】LinkedHashmap原始碼剖析Java原始碼HashMap
- 深入剖析Vue原始碼 - 來,跟我一起實現diff演算法!Vue原始碼演算法
- 介面和抽象類 (abstract)抽象
- 剖析 React 原始碼:render 流程(一)React原始碼
- 從 JSON 說起JSON
- 從Promise的Then說起Promise
- SpringMVC基礎原始碼分析(一)SpringMVC原始碼