仍是以項目實戰的形式介紹:SpringMVC-視圖解析器。最後引入網絡上對視圖解析器的講解,增強理解。html
§ View Resolver負責將處理結果生成View視圖,View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最後對View進行渲染將處理結果經過頁面展現給用戶。java
省略視圖的前綴和後綴spring
1)在springmvc.xml文件中配置視圖解析器tomcat
2)在Controller跳轉視圖的時候,能夠省略配置視圖的前綴和後綴服務器
引言: @RequestHeader、請求參數類型爲POJO(也就是Java對象類型)的狀況以及ModelAndView網絡
1. @RequestHeadermvc
這個無需多說,仍是原來的配方,仍是同樣的套路,只要舉個例子,你就都明白了。app
在SpringMVCTest中添加測試方法jsp
1
2
3
4
5
|
@RequestMapping
(value=
"/testRequestHeader"
)
public
String testRequestHeader(
@RequestHeader
(value=
"Accept-Language"
) String language){
System.out.println(
"testRequestHeader Accept-Languge:"
+ language);
return
SUCCESS;
}
|
咱們知道一個請求如get請求或post都有請求頭和響應頭,這裏咱們想獲取的是請求頭中「Accept-Language」的具體信息,因此就用上了@RequestHeader註解來獲取。ide
index.jsp中
1
|
<
a
href="springmvc/testRequestHeader">testRequestHeader</
a
><
br
/><
br
/>
|
啓動服務器,點擊超連接,咱們獲得了
1
|
testRequestHeader Accept-Languge:zh-CN
|
2. 請求參數爲POJO
前面兩篇,咱們看到的請求類型都是一些字符串也就是某一個字段。那麼若是如今有一個form表單,說誇張點,表單中有10個字段須要提交,行吧,還用原來的匹配的方式,你要用10個參數來接收,累不累?累!有沒有辦法?有!咱們能夠把這些要提交的字段封裝在一個對象中,從而請求類型就是一個POJO。
這裏咱們新建一個類User
還有一個Address類
同時咱們還須要在SpringMVCTest中寫一個testPojo的測試方法
1
2
3
4
5
|
@RequestMapping
(value=
"/testPojo"
)
public
String testPojo(User user){
System.out.println(
"testPojo: "
+ user);
return
SUCCESS;
}
|
好了,這樣,咱們就能夠在前臺jsp頁面上構造這樣的表單數據了
1
2
3
4
5
6
7
8
9
|
<
form
action="springmvc/testPojo" method="post">
username: <
input
type="text" name="username"><
br
>
password: <
input
type="password" name="password"><
br
>
email: <
input
type="text" name="email"><
br
>
age: <
input
type="text" name="age"><
br
>
city: <
input
type="text" name="address.city"><
br
>
province: <
input
type="text" name="address.province"><
br
>
<
input
type="submit" value="submit">
</
form
><
br
/><
br
/>
|
至此,咱們啓動tomcat服務器,就能夠發送一個POJO類型的參數了,而且咱們成功了讀取了這個請求參數
3. ModelAndView
ModelAndView是什麼鬼?其實它是咱們常常寫在SpringMVCTest裏測試方法的返回值類型,在方法體內咱們能夠經過ModelAndView對象來是像請求域中添加模型數據的,抽象?那就看例子吧~~~
SpringMVCTest中添加方法
1
2
3
4
5
6
7
|
@RequestMapping
(value=
"/testModelAndView"
)
public
ModelAndView testModelAndView(){
String viewname = SUCCESS;
ModelAndView modelAndView =
new
ModelAndView(viewname);
modelAndView.addObject(
"time"
,
new
Date());
return
modelAndView;
}
|
index.jsp中仍是添加一個超連接
1
|
<
a
href="springmvc/testModelAndView">testModelAndView</
a
><
br
/><
br
/>
|
注意咱們須要在結果頁面中拿到這個放入請求域中的鍵值對,因此在success.jsp頁面中添加
1
|
time: ${requestScope.time}<
br
><
br
>
|
最終的效果圖是這樣的
沒錯,咱們將當前時間信息寫進了請求域,並經過視圖展現出來。
有了前面的小鋪墊,如今咱們來嘮嘮這視圖解析器的事兒
視圖解析器
這裏主要經過調試源代碼看看spring mvc的handler是如何利用視圖解析器找到並返回實際的物理視圖的,別眨眼
1. 如何看源碼
說到調試源碼,咱們就要有源碼才行,那麼如何看源碼,相信這個頁面你們已經看膩了吧
沒錯,這是由於你沒有導入源碼的jar包,程序沒辦法給你呈現源代碼,還好,這個問題難不倒咱們,在第一篇中咱們有關於springframework所須要的功能jar包,javadoc以及源碼包,那麼來導入一波
選中前面提示的spring-context的source jar包,咱們就能夠一睹這個java文件的廬山真面目了
484很開心~~~
2. 代碼調試
爲此咱們寫一個測試方法
1
2
3
4
5
|
@RequestMapping
(
"/testViewAndViewResolver"
)
public
String testViewAndViewResolver(){
System.out.println(
"testViewAndViewResolver"
);
return
SUCCESS;
}
|
index.jsp加個連接
1
|
<
a
href="springmvc/testViewAndViewResolver">testViewAndViewResolver</
a
><
br
/><
br
/>
|
給testViewAndView方法體一個斷點,咱們進入調試狀態,
程序停在斷點處,在調試的上下文中,咱們找到DispatcherServlet.doDispaatch方法,以此爲入口,來看看視圖解析器
(1) 進入DispatcherServlet.doDispaatch
定位到
1
|
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
|
能夠看到這裏有個mv對象,實際上就是ModelAndView,經過調試咱們發現這裏的mv中包括了model和view,view的指向就是success,而model這裏之因此有值是由於在SpringMVCTest中有一個getUser方法,且加上了@ModelAttribute註解,從而初始化了model。
(2)執行processDispatchResult方法
在doDispatch中繼續執行,直到
1
|
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
|
進入該方法進行視圖渲染
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
|
private
void
processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
throws
Exception {
boolean
errorView =
false
;
if
(exception !=
null
) {
if
(exception
instanceof
ModelAndViewDefiningException) {
logger.debug(
"ModelAndViewDefiningException encountered"
, exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else
{
Object handler = (mappedHandler !=
null
? mappedHandler.getHandler() :
null
);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv !=
null
);
}
}
// Did the handler return a view to render?
if
(mv !=
null
&& !mv.wasCleared()) {
render(mv, request, response);
if
(errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else
{
if
(logger.isDebugEnabled()) {
logger.debug(
"Null ModelAndView returned to DispatcherServlet with name '"
+ getServletName() +
"': assuming HandlerAdapter completed request handling"
);
}
}
if
(WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return
;
}
if
(mappedHandler !=
null
) {
mappedHandler.triggerAfterCompletion(request, response,
null
);
}
}
|
這裏咱們着重看下render方法,而後獲得視圖的名字,即運行到view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);進入到該方法後,咱們能夠看到整個方法以下:
1
2
3
4
5
6
7
8
9
10
11
|
protected
View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request)
throws
Exception {
for
(ViewResolver viewResolver :
this
.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if
(view !=
null
) {
return
view;
}
}
return
null
;
}
|
這裏用到了視圖解析器即this.viewResolvers。而真正的渲染視圖在DispatcherServlet的view.render(mv.getModelInternal(), request, response);點擊進入這裏的render方法,咱們選擇AbstractView這個抽象類中的該方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* Prepares the view given the specified model, merging it with static
* attributes and a RequestContext attribute, if necessary.
* Delegates to renderMergedOutputModel for the actual rendering.
* @see #renderMergedOutputModel
*/
@Override
public
void
render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws
Exception {
if
(logger.isTraceEnabled()) {
logger.trace(
"Rendering view with name '"
+
this
.beanName +
"' with model "
+ model +
" and static attributes "
+
this
.staticAttributes);
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, request, response);
}
|
該方法負責針對具體的Model呈現具體的view,這時候再進入到renderMergedOutputMode的具體實現類
點擊後,咱們發現對此方法多個類都有實現,那麼究竟是哪一個呢,其實是InternalResourceView這個類,爲何定位到這個類,筆者是根據以前在springmvc.xml中配置的視圖解析器的線索找到的,當時咱們配的是InternalResourceViewResolver這個解析器,因此相應的,這裏應該是InternalResourceView類,同時經過加斷點,更加驗證了這一想法~~~
此外在調試DispatcherServlet的resolveViewName方法時,發現,這裏的viewResolver正是咱們配置的視圖解析器InternalResourceViewResolver
同時發現這裏返回的view就是/WEB-INF/views/success.jsp
至此,咱們就完成了ModelAndView的邏輯路徑向這裏"/WEB-INF/views/success.jsp"的物理路徑的轉化,大體了了解了視圖解析器的工做機制(感受仍是沒有說清楚--!)。
好了,本篇咱們主要學習了