首页 > 其他分享 >视图解析器的使用

视图解析器的使用

时间:2022-08-24 08:33:26浏览次数:50  
标签:解析器 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
From: https://www.cnblogs.com/zhenjingcool/p/16616535.html

相关文章

  • vue3+ts使用bus事件总线
    1、在vue2中我是这样使用的//创建一个vueBus.jsimportBusfrom'vue';letinstall=function(Vue){Vue.prototype.$bus=newBus()}exportdefault{install};......
  • Python-Anaconda介绍、安装及使用教程
    〇、序一、什么是Anaconda?1.简介2.特点3.Anaconda、conda、pip、virtualenv的区别①Anaconda②conda③pip④virtualenv⑤pip与conda比较→依赖......
  • 解决windows10虚拟桌面不能换桌面的问题--使用软件SylphyHorn
    GitHub:https://github.com/Grabacr07/SylphyHornwindows应用商店:https://apps.microsoft.com/store/detail/sylphyhorn/9NBLGGH58T01?hl=en-us&gl=US软件截图:效果......
  • esp32使用lvgl界面来控制四线散热风扇
    参考这位同学的文章  esp82664线风扇调速测速|OldGerman'sBlogesp32是3.3v的单片机io口可以忍受5v电平  所以如果接普通的电脑上5v的风扇不需要电平转换,加上......
  • IDEA2021.1.2版本使用Git解决代码冲突
    一、push时遇到冲突当前分支主管的推送被拒绝推送前需要合并远程更改   当前分支主管的推送被拒绝  推送前需要合并远程更改  当push时遇到冲突,要先pul......
  • IDEA2021.1.2版本使用Git
    IDEA中类的颜色红色:工作区已修改并未添加到暂存区绿色:修改已经添加到暂存区暂未提交到版本库黑色|白色:版本库与工作区一致nothingtocommit,workingtreeclea......
  • [Android开发学iOS系列] 工具篇: Xcode使用和快捷键
    [Android开发学iOS系列]工具篇:Xcode使用和快捷键工欲善其事必先利其器.编辑Cmd+N:新建文件Option+Cmd+N:新建文件夹Cmd+/:注释Ctrl+I:formatind......
  • flask 可插拔视图
    Flask0.7版本引入了可插拨视图。可插拨视图基于使用类来代替函数,其灵感来自于Django的通用视图。可插拨视图的主要用途是用可定制的、可插拨的视图来替代部分实现。普......
  • 在 C# 中使用 Span<T> 和 Memory<T> 编写高性能代码
    在C#中使用Span<T>和Memory<T>编写高性能代码  目录在C#中使用Span 和Memory 编写高性能代码.NET中支持的内存类型.NETCore2.1中新增的类型......
  • CSS的基本使用与选择器的基本使用
    CSS的基本使用与选择器的基本使用表单标签补充知识1.获取用户输入的两大重要的属性 name属性 类似于字典的键 values属性 类似于字典的值2.form表单提交数据的注......