SpringMVC-視圖解析器


SpringMVC的視圖解析器

       仍是以項目實戰的形式介紹: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"的物理路徑的轉化,大體了了解了視圖解析器的工做機制(感受仍是沒有說清楚--!)。

 

  好了,本篇咱們主要學習了

  1. @Request的用法
  2. 請求參數爲POJO的用法
  3. ModelAndView的用法
  4. 如何看源代碼
  5. spring mvc如何經過視圖解析器獲得真正的物理視圖頁面

          在寫完Spring+MyBatis+Spring MVC等文章後、我整理了一套完整實戰文檔,有須要的點擊下載

相關文章
相關標籤/搜索