javascript
学习SpringMVC——说说视图解析器
各位前排的,后排的,都不要走,咱趁熱打鐵,就這一股勁我們今天來說說spring mvc的視圖解析器(不要搶,都有位子~~~)
相信大家在昨天那篇如何獲取請求參數(shù)篇中都已經(jīng)領(lǐng)略到了spring mvc注解的魅力和套路了。搭上@RequestMapping的便車,我們可以去到我們想去的地方(方法)去,借助@RequestParam、@PathVariable等我們可以得到請求中想要的參數(shù)值,最終還能夠通過神奇的“return SUCCESS”到達(dá)我們的目的地。今天主要就來說說在達(dá)到目的地的路上,我們都經(jīng)歷了些什么!
?
在此之前
我們順便說說@RequestHeader、請求參數(shù)類型為POJO(也就是Java對象類型)的情況以及ModelAndView
1. @RequestHeader
這個無需多說,還是原來的配方,還是一樣的套路,只要舉個例子,你就都明白了。
在SpringMVCTest中添加測試方法
@RequestMapping(value="/testRequestHeader") public String testRequestHeader(@RequestHeader(value="Accept-Language") String language){System.out.println("testRequestHeader Accept-Languge:" + language);return SUCCESS; }我們知道一個請求如get請求或post都有請求頭和響應(yīng)頭,這里我們想獲取的是請求頭中“Accept-Language”的具體信息,所以就用上了@RequestHeader注解來獲取。
?
index.jsp中
<a href="springmvc/testRequestHeader">testRequestHeader</a><br/><br/>?
啟動服務(wù)器,點擊超鏈接,我們得到了
testRequestHeader Accept-Languge:zh-CN
2. 請求參數(shù)為POJO
前面兩篇,我們看到的請求類型都是一些字符串也就是某一個字段。那么如果現(xiàn)在有一個form表單,說夸張點,表單中有10個字段需要提交,行吧,還用原來的匹配的方式,你要用10個參數(shù)來接收,累不累?累!有沒有辦法?有!我們可以把這些要提交的字段封裝在一個對象中,從而請求類型就是一個POJO。
這里我們新建一個類User
?
package com.jackie.springmvc.entities;public class User {private Integer id;private String username;private String password;private String email;private int age;private Address address;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public User(String username, String password, String email, int age) {super();this.username = username;this.password = password;this.email = email;this.age = age;}public User(Integer id, String username, String password, String email, int age) {super();this.id = id;this.username = username;this.password = password;this.email = email;this.age = age;}@Overridepublic String toString() {return "User [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + ", age="+ age + "]";}public User() {} }?
還有一個Address類
package com.jackie.springmvc.entities;public class Address {private String province;private String city;public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}@Overridepublic String toString() {return "Address [province=" + province + ", city=" + city + "]";} }
同時我們還需要在SpringMVCTest中寫一個testPojo的測試方法
@RequestMapping(value="/testPojo") public String testPojo(User user){System.out.println("testPojo: " + user);return SUCCESS; }
好了,這樣,我們就可以在前臺jsp頁面上構(gòu)造這樣的表單數(shù)據(jù)了
<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服務(wù)器,就可以發(fā)送一個POJO類型的參數(shù)了,并且我們成功了讀取了這個請求參數(shù)
?
3. ModelAndView
ModelAndView是什么鬼?其實它是我們經(jīng)常寫在SpringMVCTest里測試方法的返回值類型,在方法體內(nèi)我們可以通過ModelAndView對象來是像請求域中添加模型數(shù)據(jù)的,抽象?那就看例子吧~~~
SpringMVCTest中添加方法
@RequestMapping(value="/testModelAndView") public ModelAndView testModelAndView(){String viewname = SUCCESS;ModelAndView modelAndView = new ModelAndView(viewname);modelAndView.addObject("time", new Date());return modelAndView; }
index.jsp中還是添加一個超鏈接
<a href="springmvc/testModelAndView">testModelAndView</a><br/><br/>?
注意我們需要在結(jié)果頁面中拿到這個放入請求域中的鍵值對,所以在success.jsp頁面中添加
time: ${requestScope.time}<br><br>
最終的效果圖是這樣的
沒錯,我們將當(dāng)前時間信息寫進(jìn)了請求域,并通過視圖展示出來。
?
有了前面的小鋪墊,現(xiàn)在我們來嘮嘮這視圖解析器的事兒
視圖解析器
這里主要通過調(diào)試源代碼看看spring mvc的handler是如何利用視圖解析器找到并返回實際的物理視圖的,別眨眼
1. 如何看源碼
說到調(diào)試源碼,我們就要有源碼才行,那么如何看源碼,相信這個頁面大家已經(jīng)看膩了吧
?
沒錯,這是因為你沒有導(dǎo)入源碼的jar包,程序沒辦法給你呈現(xiàn)源代碼,還好,這個問題難不倒我們,在第一篇中我們有關(guān)于springframework所需要的功能jar包,javadoc以及源碼包,那么來導(dǎo)入一波
?
選中前面提示的spring-context的source jar包,我們就可以一睹這個java文件的廬山真面目了
484很開心~~~
?
2. 代碼調(diào)試
為此我們寫一個測試方法
@RequestMapping("/testViewAndViewResolver") public String testViewAndViewResolver(){System.out.println("testViewAndViewResolver");return SUCCESS; }?
index.jsp加個鏈接
<a href="springmvc/testViewAndViewResolver">testViewAndViewResolver</a><br/><br/>?
給testViewAndView方法體一個斷點,我們進(jìn)入調(diào)試狀態(tài),
?
程序停在斷點處,在調(diào)試的上下文中,我們找到DispatcherServlet.doDispaatch方法,以此為入口,來看看視圖解析器
(1) 進(jìn)入DispatcherServlet.doDispaatch
定位到
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());?
可以看到這里有個mv對象,實際上就是ModelAndView,通過調(diào)試我們發(fā)現(xiàn)這里的mv中包括了model和view,view的指向就是success,而model這里之所以有值是因為在SpringMVCTest中有一個getUser方法,且加上了@ModelAttribute注解,從而初始化了model。
?
(2)執(zhí)行processDispatchResult方法
在doDispatch中繼續(xù)執(zhí)行,直到
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);?
進(jìn)入該方法進(jìn)行視圖渲染
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 forwardreturn;}if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}}?
這里我們著重看下render方法,然后得到視圖的名字,即運行到view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);進(jìn)入到該方法后,我們可以看到整個方法如下:
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);點擊進(jìn)入這里的render方法,我們選擇AbstractView這個抽象類中的該方法
?
/*** 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*/@Overridepublic 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);}?
?
該方法負(fù)責(zé)針對具體的Model呈現(xiàn)具體的view,這時候再進(jìn)入到renderMergedOutputMode的具體實現(xiàn)類
?
點擊后,我們發(fā)現(xiàn)對此方法多個類都有實現(xiàn),那么到底是哪個呢,實際上是InternalResourceView這個類,為什么定位到這個類,筆者是根據(jù)之前在springmvc.xml中配置的視圖解析器的線索找到的,當(dāng)時我們配的是InternalResourceViewResolver這個解析器,所以相應(yīng)的,這里應(yīng)該是InternalResourceView類,同時通過加斷點,更加驗證了這一想法~~~
此外在調(diào)試DispatcherServlet的resolveViewName方法時,發(fā)現(xiàn),這里的viewResolver正是我們配置的視圖解析器InternalResourceViewResolver
?
同時發(fā)現(xiàn)這里返回的view就是/WEB-INF/views/success.jsp
?
?
至此,我們就完成了ModelAndView的邏輯路徑向這里"/WEB-INF/views/success.jsp"的物理路徑的轉(zhuǎn)化,大致了了解了視圖解析器的工作機制(感覺還是沒有說清楚--!)。
?
好了,本篇我們主要學(xué)習(xí)了
?
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續(xù)關(guān)注我的文章,請掃描二維碼,關(guān)注JackieZheng的微信公眾號,我會將我的文章推送給您,并和您一起分享我日常閱讀過的優(yōu)質(zhì)文章。(點贊不迷路,博主帶你上高速~~~)
?友情贊助
如果你覺得博主的文章對你那么一點小幫助,恰巧你又有想打賞博主的小沖動,那么事不宜遲,趕緊掃一掃,小額地贊助下,攢個奶粉錢,也是讓博主有動力繼續(xù)努力,寫出更好的文章^^。
1. 支付寶 2. 微信
?
總結(jié)
以上是生活随笔為你收集整理的学习SpringMVC——说说视图解析器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Elasticsearch1.x 基于l
- 下一篇: 坚持每一天