ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

视图解析器的使用

2022-08-24 08:30:28  阅读:125  来源: 互联网

标签:解析器 mappedHandler request 视图 mv 使用 ModelAndView response processedRequest


 目前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响应给客户端显式。

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

标签:解析器,mappedHandler,request,视图,mv,使用,ModelAndView,response,processedRequest
来源: https://www.cnblogs.com/zhenjingcool/p/16616535.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有