day04-视图和视图解析器
视图和视图解析器
1.基本介绍
在SpringMVC中的目标方法,最终返回的都是一个视图(有各种视图)
注意,这里的视图是一个类对象,不是一个页面!!
返回的视图都会由一个视图解析器来处理(视图解析器有很多种)
2.自定义视图
2.1为什么需要自定义视图
在默认情况下,我们都是返回默认的视图,然后返回的视图交由 SpringMVC 的
InternalResourcesViewResolver
默认视图解析器来处理的:在实际开发中,因为业务需求,我们有时候需要自定义视图解析器
视图解析器可以配置多个,按照指定的顺序来对视图进行解析。如果上一个视图解析器不匹配,下一个视图解析器就会去解析视图,以此类推。
2.2应用实例
执行流程:
view.jsp,请求到 Handler
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>自定义视图测试</title>
</head>
<body>
<h1>自定义视图测试</h1>
<a href="goods/buy">点击到自定义视图</a>
</body>
</html>
GoodsHandler.java
package com.li.web.viewresolver; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; /**
* @author 李
* @version 1.0
*/
@RequestMapping(value = "/goods")
@Controller
public class GoodsHandler {
@RequestMapping(value = "/buy")
public String buy(){
System.out.println("----------buy()---------");
return "liView";//自定义视图名
}
}
自定义视图 MyView.java
package com.li.web.viewresolver; import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map; /**
* @author 李
* @version 1.0
* 1. MyView 继承了AbstractView,就可以作为了一个视图使用
* 2. @Component(value = "myView") ,该视图会注入到容器中,id为 liView
*/
@Component(value = "liView")
public class MyView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//1.完成视图渲染
System.out.println("进入到自己的视图..."); //2.并且确定我们要跳转的页面,如 /WEB-INF/pages/my_view.jsp
/*
* 1.下面就是请求转发到 /WEB-INF/pages/my_view.jsp
* 2.该路径会被springmvc解析成 /web工程路径/WEB-INF/pages/my_view.jsp
*/
request.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
.forward(request, response);
}
}
结果页面 my_view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>my_view</title>
</head>
<body>
<h1>进入到my_view页面</h1>
<p>从自定义视图来的...</p>
</body>
</html>
springDispatcherServlet-servlet.xml 配置自定义视图解析器
<!--1.指定扫描的包-->
<context:component-scan base-package="com.li.web"/> <!--2.配置视图解析器[默认的视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性 suffix(后缀) 和 prefix(前缀)-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean> <!--3.
3.1 配置自定义视图解析器 BeanNameViewResolver
3.2 BeanNameViewResolver 可以解析我们自定义的视图
3.3 属性 order 表示视图节解析器执行的顺序,值越小优先级越高
3.4 order 的默认值为最低优先级-LOWEST_PRECEDENCE
3.5 默认的视图解析器就是最低优先级,因此我们的自定义解析器会先执行
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="99"/>
</bean> <!--4.加入两个常规的配置-->
<!--支持SpringMVC的高级功能,比如:JSR303校验,映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将SpringMVC不能处理的请求,交给tomcat处理,比如css,js-->
<mvc:default-servlet-handler/>
测试,访问view.jsp,点击超链接
成功跳转到 my_view.jsp
后台输出如下:说明整个执行流程如图所示。
2.3创建自定义视图的步骤
- 自定义一个视图:创建一个 View 的 bean,该 bean 需要继承自 AbstractView,并实现renderMergedOutputModel方法
- 并把自定义 View 加入到 IOC 容器中
- 自定义视图的视图解析器,使用 BeanNameViewResolver,这个视图解析器也需要配置到 ioc 容器文件中
- BeanNameViewResolver 的调用优先级需要设置一下,设置 order 比 Integer.MAX_VALUE 小的值,以确保其在默认的视图解析器之前被调用
2.4Debug源码-自定义视图解析器执行流程
自定义视图-工作流程:
- SpringMVC 调用目标方法,返回自定义 View 在 IOC 容器中的 id
- SpringMVC 调用 BeanNameViewResolver 视图解析器:从 IOC 容器中获取返回 id 值对应的 bean,即自定义的 View 的对象
- SpringMVC 调用自定义视图的 renderMergedOutputModel 方法,渲染视图
- 说明:如果 SpringMVC 调用 Handler 的目标方法时,返回的自定义 View ,在 IOC 容器中的 id 不存在,则仍然按照默认的视图解析器机制处理。
Debug-01
(1)在GoodsHandler的目标方法中打上断点:
(2)点击debug,访问view.jsp,点击超链接,可以看到后台光标跳转到断点处:
(3)在源码 BeanNameViewResolver 的 resolveViewName 方法处打上断点:
(4)点击Resume,光标跳转到了这个断点处,viewName 的值就是自定义视图对象的 id:这里完成视图解析
resolveViewName 方法如下:
@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws BeansException {
//获取ioc容器对象
ApplicationContext context = obtainApplicationContext();
//如果容器对象中不存在 目标方法返回的自定义视图对象id
if (!context.containsBean(viewName)) {
// Allow for ViewResolver chaining...
//就返回null,让默认的视图解析器处理该视图
return null;
}
//判断自定义的视图是不是 org.springframework.web.servlet.View 类型
if (!context.isTypeMatch(viewName, View.class)) {
//如果不是
if (logger.isDebugEnabled()) {
logger.debug("Found bean named '" + viewName + "' but it does not implement View");
}
// Since we're looking into the general ApplicationContext here,
// let's accept this as a non-match and allow for chaining as well...
return null;
}
//如果是,就返回这个自定义视图对象
return context.getBean(viewName, View.class);
}
(5)在自定义视图对象里打上断点:
(6)点击 resume,光标跳转到该断点:在这里完成视图渲染,并转发到结果页面
(7)最后由 tomcat 将数据返回给客户端:
2.5Debug源码-默认视图解析器执行流程
将默认视图解析器的优先级调高:
debug-02
(1)仍然在GoodsHandler中添加断点:
(2)浏览器访问 view.jsp,可以看到后台光标跳转到了断点处:
(3)分别在默认视图解析器(InternalResourceViewResolver)和自定义视图解析器(BeanNameViewResolver) 中的方法中打上断点:
(4)点击resume,可以看到光标先跳到了默认视图解析器的 buildView 方法中:因为默认解析器的优先级在之前设置为最高。
buildView 方法:
@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//根据目标方法返回的viewName创建一个View对象
InternalResourceView view = (InternalResourceView) super.buildView(viewName);
if (this.alwaysInclude != null) {
view.setAlwaysInclude(this.alwaysInclude);
}
view.setPreventDispatchLoop(true);
return view;
}
这个 View 对象的 url 是按照你配置的前缀和后缀,拼接完成的 url
(5)之后就会到该View对象进行视图渲染,然后由Tomcat将数据返回给客户端。
但是如果该url下没有/WEB-INF/pages/liView.jsp文件,就会报错:
2.6Debug源码-自定义View不存在,会走默认视图解析机制
视图解析器可以配置多个,按照指定的顺序来对视图进行解析。如果上一个视图解析器不匹配,下一个视图解析器就会去解析视图,以此类推:
在容器文件中,将默认的视图解析器调用优先级降低,提高自定义视图解析器的调用优先级。见2.2的容器文件配置
删除2.2中的自定义视图MyView.java。也就是说,自定义视图解析器解析目标方法返回的视图对象时,将会无法解析该视图,因为它不存在。
这时就会去调用下一个优先级的视图解析器,即默认视图解析器。
debug-03
(1)仍然在GoodsHandler中添加断点:
(2)浏览器访问 view.jsp,可以看到后台光标跳转到了断点处:
(3)在自定义的视图解析器 BeanNameViewResolver 中打上断点:
(4)点击resume,可以看到光标跳转到该断点处:
(5)因为在容器文件中找不到该视图对象的id了,因此会进入第一个分支,方法直接返回 null
(6)点击step over,光标跳转到中央控制器的 resolveViewName 方法中:
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
//循环调用视图解析器,直到某个视图解析器返回的view不为null
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
因为自定义视图解析器会返回 null,因此这里进入第二次循环,由默认的视图解析器去进行解析,然后返回对应的视图:
(7)在该方法中打上断点,点击 resume,可以看到此时 view 是由默认的视图解析器返回的视图对象,走的是默认机制。
(8)下一个就按照默认机制拼接的 url 去访问该页面,并进行渲染。然后由Tomcat返回给客户端。如果根据 url 找不到该页面,就报404错误。
补充:如果默认视图解析器优先级高,自定义的视图解析器优先级低,但是默认视图解析器返回的的View为null,这时候会继续调用自定义的视图解析器吗?
答:事实上,默认视图解析器返回的 View 不会为 null。
因为它是根据目标方法返回的字符串+你配置的前后缀进行 url 的拼接。只要目标方法返回了一个字符串,默认视图处理器就不会返回 null。
如果目标方法返回的是 null 呢?将会以目标方法的路径名称+配置的前后缀作为寻找页面的 url
因此在循环调用视图处理器的时候,一旦循环到默认视图处理器,就不会调用后面的自定义视图解析器。
3.目标方法直接指定转发或重定向
3.1使用实例
目标方法中指定转发或者重定向:
- 默认返回的方式是请求转发,然后用视图处理器进行处理。
- 但是也可以在目标方法中直接指定重定向或者转发的 url 的地址。
- 注意:如果指定重定向,则不能定向到 /WEB-INF 目录中。因为该目录为 Tomcat 的内部目录。
例子
view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求转发或重定向</title>
</head>
<body>
<h1>请求转发或重定向</h1>
<a href="goods/order">测试在目标方法找中指定请求转发或重定向</a>
</body>
</html>
GoodsHandler.java
package com.li.web.viewresolver; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; /**
* @author 李
* @version 1.0
*/
@RequestMapping(value = "/goods")
@Controller
public class GoodsHandler {
//演示直接指定请求转发或者重定向
@RequestMapping(value = "/order")
public String order() {
System.out.println("=========order()=========");
//请求转发到 /WEB-INF/pages/my_view.jsp
//下面的路径会被解析/web工程路径/WEB-INF/pages/my_view.jsp
return "forward:/WEB-INF/pages/my_view.jsp";
}
}
访问 view.jsp,点击超链接:
成功进入到请求转发的页面:
请求转发也可以转发到WEB-INF目录之外的页面。
修改 GoodsHandler.java 的 order 方法:
//演示直接指定请求转发或者重定向
@RequestMapping(value = "/order")
public String order() {
System.out.println("=========order()=========");
//重定向
//1.对于重定向来说,不能重定向到/WEB-INF/目录下
//2.redirect 为重定向的关键字
//3./login.jsp 是在服务器解析的,解析为 /web工程路径/login.jsp
return "redirect:/login.jsp";
}
redeployTomcat,访问 view.jsp,点击超链接,可以看到成功重定向到 login.jsp页面
3.2Debug-指定请求转发流程分析
day04-视图和视图解析器的更多相关文章
- SpringMVC听课笔记(六:视图和试图解析器)
1.spring mvc解析视图 2. 视图和视图解析器 3. 视图 4.常用的视图类 5.视图解析器 1) 2) 3) 4)JSTL 需要注意的是,配置了mvc:view-controller,为 ...
- Spring MVC的多视图解析器配置及与Freemarker的集成
一.从freemarker谈起 Freemarker使用模板技术进行视图的渲染.自从看了Struts标签.Freemarker.JSTL的性能对比后,我毅然决定放弃Struts标签了!效率太差…… S ...
- DRF之频率限制、分页、解析器和渲染器
一.频率限制 1.频率限制是做什么的 开放平台的API接口调用需要限制其频率,以节约服务器资源和避免恶意的频繁调用. 2.频率组件原理 DRF中的频率控制基本原理是基于访问次数和时间的,当然我们可以通 ...
- rest-framework之解析器
解析器 解析器的作用 根据请求头 content-type 选择对应的解析器对请求体内容进行处理. 有application/json,x-www-form-urlencoded,form-data等 ...
- drf8 解析器
解析器的介绍 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己想要的数据类型的过程. 本质就是对请求体中的数据进行解析. Accept与ContentType请求头. Accept是告诉 ...
- DRF频率、分页、解析器、渲染器
DRF的频率 频率限制是做什么的 开放平台的API接口调用需要限制其频率,以节约服务器资源和避免恶意的频繁调用. 频率组件原理 DRF中的频率控制基本原理是基于访问次数和时间的,当然我们可以通过自己定 ...
- Django-Rest-Framework的解析器和渲染器
Django-Rest-Framework的解析器和渲染器 restful framework 解析器 解析器的作用就是服务端接收客户端传来的数据,把数据解析成自己想要的数据类型的过程 本质就是对请 ...
- 【DRF解析器和渲染器】
目录 解析器 Django中的解析器 DRF中的解析器 DRF中的渲染器 @ *** 解析器 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己想要的数据类型的过程. 本质就是对请求体中的 ...
- rest framework 之解析器
一.示例 1.api/urls.py from django.urls import path, re_path from api.views import UserView, ParserView ...
- SpringMVC视图解析器
SpringMVC视图解析器 前言 在前一篇博客中讲了SpringMVC的Controller控制器,在这篇博客中将接着介绍一下SpringMVC视 图解析器.当我们对SpringMVC控制的资源发起 ...
随机推荐
- 论文笔记 - Noisy Channel Language Model Prompting for Few-Shot Text Classification
Direct && Noise Channel 进一步把语言模型推理的模式分为了: 直推模式(Direct): 噪声通道模式(Noise channel). 直观来看: Direct ...
- VBA工程设置密码
VBA 工程设置密码 Alt + F11,进入程序界面: 工具---> VBAProject属性---> 保护---> 查看时锁定工程前打勾,并在下面的密码区输入密码.
- 「浙江理工大学ACM入队200题系列」问题 L: 零基础学C/C++52——计算数列和2/1,3/2,5/3,8/5......
本题是浙江理工大学ACM入队200题第五套中的L题 我们先来看一下这题的题面. 题面 题目描述 有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13,-- 计算这个数列的前n项和.注意: ...
- 【JAVA】详解在JAVA中int与Integer的区别以及背后的原因。
区别 首先我们要明确,这两点之间有什么区别? 主要有以下几点: 数据类型不同:int是基础数据类型,而 Integer是包装数据类型: 默认值不同:int的默认值是 0,而 Integer的默认值是 ...
- Crond服务+Shell实现秒级任务
服务 [root@19-v1-centos-6 ~]# chkconfig --list | grep crond crond 0:off 1:off 2:on 3:on 4:on 5:on 6:of ...
- Karmada跨集群优雅故障迁移特性解析
摘要:在 Karmada 最新版本 v1.3中,跨集群故障迁移特性支持优雅故障迁移,确保迁移过程足够平滑. 本文分享自华为云社区<Karmada跨集群优雅故障迁移特性解析>,作者:Karm ...
- 《MySQL必知必会》知识汇总一
一.使用MYSQL 展示所有数据库 show databases; 选择数据库 use crashcourse; 展示该数据库中所有的表 show tables; 还可以展示表列的shema约束 sh ...
- 【Java EE】Day11 BootStrap、响应式布局、栅格系统、CSS样式、案例
一.BootStrap介绍 https://v3.bootcss.com/css/#overview 1.概念 基于三剑客开发的前端开发框架 定义了许多css样式和js插件,从而得到丰富的页面效果 依 ...
- PostgreSQL和MySQL的优劣对比
在开发项目的过程中,难免要面对选择数据库的情况.总结此文章是因为在之前公司里使用的都是MYSQL 数据库,而在现在公司里,新项目中使用的是 PostgreSQL 数据库,在使用过程中,经常需要查找两种 ...
- Django中ORM多对多三种创建方式(全自动-纯手动-半自动)
一:多对多三种创建方式 1.全自动: 利用orm自动帮我们创建第三张关系表 class Book(models.Model): name = models.CharField(max_length=3 ...