视图解析器的使用

 目前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方法,直接返回。请求结束,没有使用视图解析器。

posted @ 2022-08-24 08:21  zhenjingcool  阅读(248)  评论(0编辑  收藏  举报