仍是以项目实战的形式介绍: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"的物理路径的转化,大体了了解了视图解析器的工做机制(感受仍是没有说清楚--!)。
好了,本篇咱们主要学习了