第七章 视图解析 方法的返回(视图名viewName)

找包Ctrl + Shift + T

forward前缀指定一个转发操作

转发代码 前缀会特殊处理
forward:前缀的转发,不会由我们配置的视图解析器拼串

package com.project.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
    // return "/hello" 会自动拼接/WEB-INF/jsp/hello.jsp  
    @RequestMapping("/hello")
    public String hello() {
        return "../../hello";//相对路径的形式才可以访问-> /hello.jsp
    }
    @RequestMapping("/handle01")
    public String handle01() {
        System.out.println("01");
        return "forward:/hello.jsp";
    }
    @RequestMapping("/handle02")
    public String handle02() {
        System.out.println("02");
        return "forward:/handle01";
    }
}

效果图
图片说明

重定向 redirect

关于重定向

  • 一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理
  • 如果返回的字符串中带 forward: 或 redirect: 前缀时,SpringMVC 会对他们进行特殊处理:将 forward: 和 redirect: 当成指示符,其后的字符串作为 URL 来处理
  • redirect:/success.jsp:会完成一个到 success.jsp 的重定向的操作
  • forward:/success.jsp:会完成一个到 success.jsp 的转发操作

原生的Servlet重定向/路径需要加上重定向

@Controller
public class MyController {
    // return "/hello" 会自动拼接/WEB-INF/jsp/hello.jsp  
    @RequestMapping("/hello")
    public String hello() {
        return "../../hello";//相对路径的形式才可以访问-> /hello.jsp
    }
    @RequestMapping("/handle01")
    public String handle01() {
        System.out.println("forward01");
        return "forward:/hello.jsp";
    }
    @RequestMapping("/handle02")
    public String handle02() {
        System.out.println("forward02");
        return "forward:/handle01";
    }
    @RequestMapping("/handle03")
    public String handle03() {
        System.out.println("redirect03");
        return "redirect:/hello2.jsp";
    }
    @RequestMapping("/handle04")
    public String handle04() {
        System.out.println("redirect04");
        return "redirect:/handle02";
    }
}
  • 效果1
    图片说明
  • 效果2
    图片说明

视图解析

方法执行后的返回值会作为页面地址参考,转发或者重定向到页面
视图解析器可能会进行页面地址的拼串

  • 任何方法的返回值,最终都会被被包装成ModelAndView对象
    图片说明

  • 处理来到页面方法 视图渲染:将域中的数据在页面展示,页面就是用来渲染模型数据的; 转发或者重定向
    图片说明

  • 调用render渲染页面
    图片说明

  • View与ViewResolver(接口 且只有下图是它的接口方法),ViewResolver的作用是根据视图名(方法的返回值)得到View对象
    图片说明
    resolveViewName跟上面一样
    图片说明
    mv.getViewName = "../../hello"【采用视图拼接 需要考虑拼接情况】
    mv.getModelInternal()=>是隐含模型的所有数据

  • 怎么能根据方法的返回值视图名viewName)得到View对象 到了resolveViewName方法 遍历所有viewResolvers【我们有配置,默认也是InternalResourceViewResolver】 如果得到不为空 直接返回 return view 跳出循环
    图片说明

    图片说明

    图片说明
    默认配置的 在DispatchServlet.properties
    图片说明

  • 第一次启动项目是没有view的 需要创建【先去缓存中找一下有没有 为空就create】 下面的部分是viewResolver.resolveViewName方法实现
    图片说明

  • 创建view对象 分别针对重定向或者转发创建 我们首先默认是第三种
    图片说明

  • 接下来对字符串进行拼接
    图片说明

  • 解析完成得到 【view接口看下面有】 然后返回view对象 并放在缓存中
    图片说明
    图片说明

下面概述继续讲解view对象的处理

SpringMVC如何解析视图概述

  1. 视图解析器得到view对象的流程就是,所有配置的视图解析器都来尝试根据视图名(返回值)得到View(视图)对象;如果能得到就返回,得不到就换下一个视图解析器【https://blog.csdn.net/qq_38409944/article/details/82783021】
  2. 调用View对象的render方法
    图片说明
  3. 将模型中的数据全部取出放在request域中
    图片说明
  4. 拿到转发器
    图片说明
  5. 进行转发
    图片说明

视图解析器只是为了得到视图对象;视图对象才能真正的转发(将模型数据全部放在请求域中)或者重定向到页面,视图对象才能真正渲染视图

view对象
图片说明

视图解析源码分析:重要的两个接口
图片说明
图片说明

流程图
图片说明

不论控制器返回一个String,ModelAndView,View都会转换为ModelAndView对象,由视图解析器解析视图,然后,进行页面的跳转
图片说明

视图和视图解析器

  • 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图【视图信息:要去的页面 模型数据:要展示的数据】
  • Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View)【不同的视图对象具有不同的功能】【真正渲染数据】,最终的视图可以是 JSP ,也可能是 Excel、JFreeChart等各种表现形式的视图
  • 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦

视图

  • 视图的作用是渲染模型数据,将模型里的数据以某种形式【转发只是一种 下载也是】呈现给客户。
  • 为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口
    图片说明
  • 视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题【上一次的跟下一次无关】

常用的视图实现类

图片说明

视图解析器

  • SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。
  • 视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。
  • 所有的视图解析器都必须实现 ViewResolver 接口:
    图片说明
    根据返回值得到view对象

常用的视图解析器实现类

多个视图循环解析
图片说明

  • 程序员可以选择一种视图解析器或混用多种视图解析器
  • 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可以通过 order 属性指定解析器的优先顺序,order越小优先级越高
  • SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常
  • InternalResourceViewResolver
  • JSP 是最常见的视图技术,可以使用 InternalResourceViewResolve作为视图解析器:
    图片说明

国际化

只要导入了jstl的jar包,以前默认创建的InternalResouceView都会被使用jstlView替代:https://www.cnblogs.com/limingxian537423/p/7282213.html

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
    </bean>

图片说明
导包的时候会自动创建为一个jstlView 可以快速方便的支持国际化

JstlView 是 InternalResourceView的子类 功能更强大

图片说明

  • 若项目中使用了JSTL,则SpringMVC 会自动把视图由InternalResourceView转为 JstlView (断点调试,将JSTL的jar包增加到项目中,视图解析器会自动修改为JstlView)
  • 若使用 JSTL 的 fmt 标签则需要在 SpringMVC 的配置文件中配置国际化资源文件
    图片说明
  • 若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-controller 标签实现
    图片说明

代码

  • 原始jsp页面的代码

    《form action="">
      用户名:《input />
      密码:《input />
      《input type="submit" value="登陆"/>
    《/form>

    图片说明

  • 改进后代码为:

    《i18n_en_US.properties》
    welcomeinfo=WELECOME TO HERE
    username=USERNAME
    password=PASSWORD
    loginBtn=LOGIN
    
    《i18n_zh_CN.properties》
    welcomeinfo=\u6B22\u8FCE\u6765\u5230\u8FD9\u91CC
    username=\u7528\u6237\u540D
    password=\u5BC6\u7801
    loginBtn=\u767B\u9646
    
    jsp部分
      《%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    《fmt:message key="welcomeinfo">《/fmt:message>
    《form action="">
      《fmt:message key="username"/>:《input />
      《fmt:message key="password"/>:《input />
      《input type="submit" value='《fmt:message key="loginBtn"/>'/>
    《/form>
    
    spring部分
    
      <!-- messageSource 必需这个名字 且第一个字母要小写 否则读取不到 -->
      <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
          <property name="basename" value="i18n"></property>
      </bean>
  • 效果
    图片说明
    为啥要给bean的名字一定要为messageSource 因为
    图片说明

  • 增加jstl标签 jar包(断点调试,这时的View对象就是JstlView)
    图片说明

  • 坑1 直接运行jsp代码 避开了spring的管理 效果 如下
    图片说明

  • 坑2 forward
    图片说明
    原因
    图片说明

mvc:view-controller标签

发送一个请求

  • 若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-controller 标签实现
    <!-- 直接配置响应的页面:无需经过控制器来执行结果 -->
    <mvc:view-controller path="/success" view-name="success"/>
  • 请求的路径
    http://localhost:8080/SpringMVC_02_View/hello 
    http://localhost:8080/SpringMVC_02_View/toLogin 
  • 控制器代码
      @RequestMapping("/hello")
      public String hello() {
          return "forward:/WEB-INF/jsp/hello.jsp";
      }
  • 配置《mvc:view-controller>会导致其他请求路径失效
    图片说明
  • 解决办法:
    <!-- 在实际开发过程中都需要配置mvc:annotation-driven标签,后面讲,这里先配置上 -->
    <!--开启mvc注解驱动模式 ,开启了mvc的开挂模式-->
    <mvc:annotation-driven/>

扩展

加深视图解析器和视图对象;
视图解析器根据方法的返回值得到视图对象
多个视图解析器都会尝试能否得到视图对象
视图对象不同就可以具有不同的功能

自定义视图

这里需要考虑遍历顺序
图片说明

  • 自定义视图(需要加入SpringMVC,那么,一定需要实现框架的接口)
  • 若希望使用 Excel 展示数据列表,仅需要扩展 SpringMVC 提供的 AbstractExcelView 或 AbstractJExcelView 即可。
  • 实现 buildExcelDocument() 方法,在方法中使用模型数据对象构建 Excel 文档就可以了。
  • AbstractExcelView 基于 POI API,而 AbstractJExcelView 是基于 JExcelAPI 的。
  • 视图对象需要配置 IOC 容器中的一个 Bean,使用 BeanNameViewResolver 作为视图解析器即可
  • 若希望直接在浏览器中直接下载 Excel 文档,则可以设置响应头 Content-Disposition 的值为 attachment;filename=xxx.xls
    图片说明

控制器代码:MyViewResovlerController/发请求

@Controller
public class MyViewResovlerController {
    @RequestMapping("/handlerplus")
    public String handlerplus(Model model) {
        List<String> vname = new ArrayList<String>();
        vname.add("陈志男");
        vname.add("陈耿聪");
        vname.add("王楚銮");
        model.addAttribute("video", vname);
        System.out.println("发起请求");
        return "cznczai:/hello";
    }
}

自定义视图处理器的代码MyViewResolver

public class MyViewResolver implements ViewResolver {
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        if(viewName.startsWith("cznczai:")) {
            System.out.println("来到自定义视图处理器MyViewResolver");
            //根据视图名返回视图对象
            return new MyView(); 
        }
        else {
            return null;
        }
    }
}

到ioc里注册

<bean id="myViewResolver" class="com.project.view.MyViewResolver">
    </bean>

视图实现类的代码 : 不需要注册到ioc中 因为视图解析器器有帮忙new一个view

public class MyView implements View {
    /**
     * 返回的数据的内容类型
     */
    @Override
    public String getContentType() {
        return "text/html";
    }
    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        System.out.println("来到MyView");
        System.out.println("之前保存的数据"+model);
        response.getWriter().write("你好,自定义视图");
    }
}

图片说明
效果 也就是先运行了InternalResourceViewResolver 【配置文件没有解析器也会默认找这个】
图片说明
修改视图解析器的优先级 InternalResourceViewResolver 默认优先级最小
图片说明
对代码进行升级
对MyView增加了字符乱码的处理

public class MyView implements View {
    /**
     * 返回的数据的内容类型
     */
    @Override
    public String getContentType() {
        return "text/html";
    }
    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        System.out.println("来到MyView");
        System.out.println("之前保存的数据"+model);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("UTF-8");
        response.getWriter().write("你好,自定义视图");
//response.getWriter().write 可以增加脚本或者其他html代码
    }
}

对视图解析器增加了ordered的处理

public class MyViewResolver implements ViewResolver ,Ordered{
    private Integer order = 0;
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        if(viewName.startsWith("cznczai:")) {
            System.out.println("来到自定义视图处理器MyViewResolver");
            //根据视图名返回视图对象
            return new MyView(); 
        }
        else {
            return null;
        }
    }
    @Override
    public int getOrder() {
        return order;
    }
    public void setOrder(Integer order) {
        this.order = order;
    }
}

图片说明

全部评论

相关推荐

最近和朋友聊天,她说了句让我震惊的话:"我发现我连周末点外卖都开始'最优解'了,一定要赶在高峰期前下单,不然就觉得自己亏了。"这不就是典型的"班味入侵"吗?工作思维已经渗透到生活的方方面面。
小型域名服务器:啊?我一直都这样啊?我还以为是我爱贪小便宜呢?每次去实验室都得接一杯免费的开水回去,出门都得规划一下最短路径,在宿舍就吃南边的食堂,在实验室就吃北边的食堂,快递只有顺路的时候才取。
点赞 评论 收藏
分享
过往烟沉:我说什么来着,java就业面就是广!
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务