目前web应用都是使用前后端分离的开发方式,在这种方式下,其实不会用到springmvc的视图解析器。
官网上有这么一段话:
An appropriate handler is searched for. If a handler is found, the execution chain associated with the handler (preprocessors, postprocessors, and controllers) is run to prepare a model for rendering. Alternatively, for annotated controllers, the response can be rendered (within the HandlerAdapter) instead of returning a view.
If a model is returned, the view is rendered. If no model is returned (maybe due to a preprocessor or postprocessor intercepting the request, perhaps for security reasons), no view is rendered, because the request could already have been fulfilled.
大致意思是,DispatcherServlet实例会查找一个handler,然后构造一个执行链,然后会执行这个执行链来准备model,以备进行渲染,并且返回一个视图供后续处理。但是如果带注解的controller,响应会在处理器适配器中进行渲染,而不是返回一个view
也就是说,如果我们要研究视图解析器,我们不能使用带注解的controller
1 使用视图解析器的场景
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- <context:component-scan base-package="com.szj"/>-->
<!-- <mvc:annotation-driven />-->
</beans>
注释掉上面两行
dispatcher-servlet.xml
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/jsp/"/><!--设置JSP文件的目录位置-->
<property name="suffix" value=".jsp"/>
</bean>
<bean id="/hello" class="com.szj.controller.JspController" />
JspController.java
public class JspController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv=new ModelAndView();
//封装对象,放在ModelAndView中
mv.addObject("msg", "Hello!SpringMVC!~~");
//封装要跳转的视图,放在ModelAndView中。
mv.setViewName("hello"); /jsp/hello.jsp
return mv;
}
}
hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
然后我们请求/hello这个链接
我们知道,请求会首先到达DispatcherServlet的如下方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
ModelAndView mv = null;
Exception dispatchException = null;
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mappedHandler.applyPreHandle(processedRequest, response)
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
首先会获取一个处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
此时的处理器适配器是SimpleControllerHandlerAdapter实例。
接下来,处理器适配器将处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
执行的是SimpleControllerHandlerAdapter的handle方法
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
((Controller) handler).handleRequest(request, response); 这里强制转为对应的Controller类,然后调用的是JspController的如下方法,该方法是Controller接口的重写方法。
public class JspController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv=new ModelAndView();
//封装对象,放在ModelAndView中
mv.addObject("msg", "Hello!SpringMVC!~~");
//封装要跳转的视图,放在ModelAndView中。
mv.setViewName("hello"); //WEB-INF/jsp/hello.jsp
return mv;
}
}
再回到DispatcherServlet的doDispatch方法,
处理器适配器调用执行链,最终返回一个ModelAndView对象mv,然后执行如下代码processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
其代码为
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
if (mv != null) {
render(mv, request, response);
}
}
如果mv为空,将直接返回,带注解的Controller就属于这种情况。
这里mv不为空,将调用 render(mv, request, response)
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
view.render(mv.getModelInternal(), request, response);
}
这里首先生成一个View对象,然后调用 view.render 方法
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
接着调用如下方法
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
rd.forward(request, response);
}
这里的RequestDispatcher对象rd是Tomcat中的对象,至此,springmvc处理阶段结束,Tomcat将会继续处理这个请求并且把jsp响应给客户端显式。
注意:ViewResolver负责视图名和视图之间的映射(比如视图名hello对应的视图是/WEB-INF/hello.jsp),View负责准备请求,并调用view.render()将渲染交由特定的视图技术实现。Servlet容器默认支持JSP视图技术,所以这里我们可以看到,调用view.render()后,其实视图渲染是交给Servlet容器进行的,我们看不到具体的渲染过程。如果这里我们使用的是别的Servlet容器默认不支持的视图技术,情况将不同,我们可以看到视图被渲染的全过程。比如FreeMarkerView,执行render方法后,最终调用的是如下方法:
protected void doRender(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose all standard FreeMarker hash models.
SimpleHash fmModel = buildTemplateModel(model, request, response);
// Grab the locale-specific version of the template.
Locale locale = RequestContextUtils.getLocale(request);
processTemplate(getTemplate(locale), fmModel, response);
}
在这个方法中,我们可以看到它获取模板,获取数据,并渲染。
2 不使用视图解析器的场景
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.szj"/>
<mvc:annotation-driven />
</beans>
去掉注释
新增一个Controller
@Controller
@RequestMapping("/index")
public class MainController {
@RequestMapping (value = "/main.json",method = RequestMethod.GET)
@ResponseBody
public Object main(){
JSONObject json = new JSONObject();
Map map = new HashMap();
map.put("age",28);
map.put("sex",1);
map.put("name","szj");
json.put("value","key");
json.put("age",12);
json.put("list",map);
String pretty = JSON.toJSONString(json, SerializerFeature.PrettyFormat, SerializerFeature.WriteDateUseDateFormat);
System.out.println(pretty);
return pretty;
}
}
其余都和上例一样
我们访问http://localhost:8080/springmvc_demo_war_exploded/index/main.json
同样请求到达DispatcherServlet的如下方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
ModelAndView mv = null;
Exception dispatchException = null;
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mappedHandler.applyPreHandle(processedRequest, response)
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
和上面不同,此时的处理器适配器是RequestMappingHandlerAdapter实例。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 执行的是RequestMappingHandlerAdapter的如下方法
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
然后调用如下方法
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
其中 invocableMethod.invokeAndHandle(webRequest, mavContainer); 最终调用的是我们写的Controller,且在这个适配器里面直接处理响应,并且设置mavContainer的requestHandled=true表示请求已处理。
然后执行 return getModelAndView(mavContainer, modelFactory, webRequest);
如下是这个方法的定义
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
...省略
}
这里判断 mavContainer.isRequestHandled() 是否已经处理过了,这里为true,返回null
我们再返回DispatcherServlet的doDispatch方法
继续执行
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
这里的mv是null
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
}
}
由于mv是null,将不会执行render方法,直接返回。请求结束,没有使用视图解析器。