视图和视图解析器
- 💞基本介绍
- 💞 自定义视图
- 为什么需要自定义视图
- 自定义试图实例-代码实现
- 自定义视图工作流程小结
- Debug源码
- 默认视图解析器执行流程
- 多个视图解析器执行流程
- 💞目标方法直接指定转发或重定向
- 使用实例
- 指定请求转发流程-Debug源码
- 指定重定向流程-Debug源码
- 课后作业
上一讲, 我们学习的是SpringMVC系列五: SpringMVC映射请求数据
现在打开springmvc项目
💞基本介绍
1.在SpirngMVC
中的目标方法最终返回都是一个视图(有各种视图)
2.返回的视图都会由一个视图解析器来处理(视图解析器有很多种)
💞 自定义视图
为什么需要自定义视图
1.在默认情况下, 我们都是返回默认的视图, 然后返回的视图交由SpringMVC
的InternalResourceViewResolver
视图解析器来处理的.
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性prefix 和 suffix-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
2.在实际开发中, 我们有时需要自定义视图, 这样可以满足更多更复杂的需求.
自定义试图实例-代码实现
1.创建web路径/view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>自定义视图测试</title>
</head>
<body>
<h2>自定义视图测试</h2>
<a href="?">点击到自定义视图</a>br/><br/>
</body>
</html>
2.创建com.zzw.web.viewresolver.GoodsHandler.java
@RequestMapping("/goods")
@Controller
public class GoodsHandler {
@RequestMapping("/buy")
public String buy() {
System.out.println("buy() 被调用...");
return "?";//待会再填写自定义视图名称
}
}
3.创建comzzw.web.viewresolver.MyView.java
自定义视图类
/**
* 解读
* 1.MyView 继承了AbstractView, 就可以作为一个视图使用
* 2.@Component(value="myView"), 该视图会注入到容器中, 名字/id 是 zzwView
*/
@Component(value = "zzwView")
public class MyView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//完成视图渲染
//并且可以确定我们要跳转的页面
System.out.println("进入到自己的视图");
}
}
4.创建web路径/WEB-INF/pages/my_view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>my_view页面</title>
</head>
<body>
<h2>进入到my_view页面</h2>
<p>是从自定义视图来的</p>
</body>
</html>
5.补充MyView
的代码
/**
* 解读
* 1.MyView 继承了AbstractView, 就可以作为一个视图使用
* 2.@Component(value="myView"), 该视图会注入到容器中, 名字/id 是 zzwView
*/
@Component(value = "zzwView")
public class MyView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
//完成视图渲染
//并且可以确定我们要跳转的页面[请求转发] /WEB-INF/pages/my_view.jsp
System.out.println("进入到自己的视图");
//解读
//1.下面就是进行请求转发到 /WEB-INF/pages/my_view.jsp
//2./WEB-INF/pages/my_view.jsp 会被springmvc解析
// /springmvc/WEB-INF/pages/my_view.jsp
request.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
.forward(request, response);
}
}
6.配置springDispatcherServlet-servlet.xml
, 增加自定义视图解析器
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性prefix 和 suffix-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--
解读
1.配置自定义视图解析器BeanNameViewResolver
2.BeanNameViewResolver可以去解析我们自定义的视图
3.配置 属性 order, 表示视图解析器执行的顺序, 值越小, 优先级越高
4.属性 order 的默认值是最低优先级, 值为 Integer.MAX_VALUE
int LOWEST_PRECEDENCE = 2,147,483,647;
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="99"/>
</bean>
7.view.jsp
修改请求路径
<h2>自定义视图测试</h2>
<a href="goods/buy">点击到自定义视图</a>br/><br/>
8.GoodsHandler.java
完善代码
@RequestMapping("/goods")
@Controller
public class GoodsHandler {
@RequestMapping("/buy")
public String buy() {
System.out.println("buy() 被调用...");
return "zzwView";//待会再填写自定义视图名称
}
}
9.Postman测试
自定义视图工作流程小结
●自定义视图-小结
1.创建一个View
的bean
, 该bean
需要继承自AbstractView
, 并实现renderMergedOutputModel
方法
2.并把自定义View
加入到IOC
容器中
3.自定义视图的视图处理器, 使用BeanNameViewResolver
, 这个视图处理器也需要配置到ioc
容器
4.BeanNameViewResolver
的调用优先级需要设置一下, 设置order
比Integer.MAX_VALUE
小的值, 以确保其在InternalResourceViewResolver
之前被调用
●自定义视图-工作流程
1.SpringMVC
调用目标方法, 返回自定义View
在IOC
容器中的id
2.SpringMVC
调用BeanNameViewResolver
解析视图: 从IOC
容器中获取 返回id
值对应的bean
, 即自定义View
的对象
3,SpringMVC
调用自定义视图的renderMergedOutputModel
方法渲染视图
4.说明:
如果在SpringMVC
调用目标方法, 返回自定义View
容器中的id
, 不存在, 则仍然按照默认的视图处理器机制处理.
Debug源码
判断是否实现了View接口, 如果实现了, 返回View对象
在MyView视图打个断点
进入到my_view.jsp
默认视图解析器执行流程
1.配置默认视图解析器的优先级
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性prefix 和 suffix-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
<!--调整优先级-->
<property name="order" value="10"/>
</bean>
2.debug源码
进入到默认视图解析器
由于没有zzwView.jsp页面, 页面会报错
多个视图解析器执行流程
案例1: 假设自定义视图解析器的优先级大于默认视图解析器
1.将默认视图解析器的优先级大小设置为默认
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性prefix 和 suffix-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
<!--调整优先级-->
<!--<property name="order" value="10"/>-->
</bean>
2.将自定义视图MyView.java
的beanName改为zzwViewx
/**
* 解读
* 1.MyView 继承了AbstractView, 就可以作为一个视图使用
* 2.@Component(value="myView"), 该视图会注入到容器中, 名字/id 是 zzwView
*/
@Component(value = "zzwViewx")
public class MyView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
//完成视图渲染
//并且可以确定我们要跳转的页面[请求转发] /WEB-INF/pages/my_view.jsp
System.out.println("进入到自己的视图");
//解读
//1.下面就是进行请求转发到 /WEB-INF/pages/my_view.jsp
//2./WEB-INF/pages/my_view.jsp 会被springmvc解析
// /springmvc/WEB-INF/pages/my_view.jsp
request.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
.forward(request, response);
}
}
3.debug源码
在容器中找不到id为zzwView
的bean对象. 参数viewName
是zzwView
,
BeanName视图解析器返回了空, 开始循环第二个默认视图解析器
对返回的view对象进行估算. 没有 /WEB-INF/pages/zzwView.jsp 这个文件, 会报404错误.
案例2: 假设默认视图解析器的优先级大于自定义视图解析器
1.将默认视图解析器的优先级大小设置为10, 此时自定义视图解析器的优先级是99.
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性prefix 和 suffix-->
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
<!--调整优先级-->
<property name="order" value="10"/>
</bean>
2.将自定义视图MyView.java的beanName改为zzwView
/**
* 解读
* 1.MyView 继承了AbstractView, 就可以作为一个视图使用
* 2.@Component(value="myView"), 该视图会注入到容器中, 名字/id 是 zzwView
*/
@Component(value = "zzwView")
public class MyView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
//完成视图渲染
//并且可以确定我们要跳转的页面[请求转发] /WEB-INF/pages/my_view.jsp
System.out.println("进入到自己的视图");
//解读
//1.下面就是进行请求转发到 /WEB-INF/pages/my_view.jsp
//2./WEB-INF/pages/my_view.jsp 会被springmvc解析
// /springmvc/WEB-INF/pages/my_view.jsp
request.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
.forward(request, response);
}
}
3.debug源码
只要执行了默认视图解析器, 找不到页面, 会报404, 不会再执行下一个视图解析器.
💞目标方法直接指定转发或重定向
使用实例
● 目标方法中指定转发或者重定向
1.默认返回的方法是请求转发, 然后用视图处理器进行处理, 比如在目标方法中这样写:
2.也可以在目标方法中直接指定重定向或请求转发的url
地址
3.如果指定重定向, 可以定向到web路径下的资源
但是有个例外: 不能重定向到/WEB-INF
目录.
原因: WEB-INF下的资源是服务器内部访问的, 浏览器不能访问
案例
1.修改com.zzw.web.viewresolver.GoodsHandler.java
, 增加方法order
@RequestMapping("/goods")
@Controller
public class GoodsHandler {
@RequestMapping("/order")
public String order() {
System.out.println("===========order()===========");
//1.请求转发到 /WEB-INF/pages/my_view.jsp
//2.下面的这个路径 /WEB-INF/pages/my_view.jsp
// 会被解析成 /springmvc/WEB-INF/pages/my_view.jsp
//return "forward:/WEB-INF/pages/my_view.jsp";
//1.请求转发到 /aa/bb/ok.jsp
//2.下面的这个路径 /aa/bb/ok.jsp
// 会被解析成 /springmvc/aa/bb/ok.jsp
//return "forward:/aa/bb/ok.jsp";
/**
* 直接指定要重定向的页面
* 1. 对于重定向来说, 不能重定向到 /WEB-INF/ 目录下
* 2. redirect 关键字, 表示进行重定向
* 3. /login.jsp 会在服务器解析成 /springmvc/login.jsp
*/
//return "redirect:/login.jsp";
//重定向到 /WEB-INF/pages/my_view.jsp
//下面的这个路径 /WEB-INF/pages/my_view.js 会在服务器解析成
// /springmvc/WEB-INF/pages/my_view.jsp
return "redirect:/WEB-INF/pages/my_view.jsp";
}
}
2.view.jsp
增加代码
<body>
<h2>自定义视图测试</h2>
<a href="goods/buy">点击到自定义视图</a><br/><br/>
<a href="goods/order">测试在目标方法中指定请求转发或者重定向到页面</a>
</body>
测试1
//1.请求转发到 /WEB-INF/pages/my_view.jsp
//2.下面的这个路径 /WEB-INF/pages/my_view.jsp 会被解析成 /springmvc/WEB-INF/pages/my_view.jsp
return "forward:/WEB-INF/pages/my_view.jsp";
测试2
//1.请求转发到 /aa/bb/ok.jsp
//2.下面的这个路径 /aa/bb/ok.jsp 会被解析成 /springmvc/aa/bb/ok.jsp
return "forward:/aa/bb/ok.jsp";
测试3
/**
* 直接指定要重定向的页面
* 1. 对于重定向来说, 不能重定向到 /WEB-INF/ 目录下
* 2. redirect 关键字, 表示进行重定向
* 3. /login.jsp 会在服务器解析成 /springmvc/login.jsp
*/
return "redirect:/login.jsp";
测试4
//1.重定向到 /WEB-INF/pages/my_view.jsp
//2.下面的这个路径 /WEB-INF/pages/my_view.js 会在服务器解析成 /springmvc/WEB-INF/pages/my_view.jsp
return "redirect:/WEB-INF/pages/my_view.jsp";
WEB-INF下的资源是服务器内部访问的, 浏览器不能访问
指定请求转发流程-Debug源码
debug源码
调用默认的视图解析器
已经作为一个bean对象存储在容器中
下一步, 开始渲染
下一步
进入
进入, renderMergedOutputModel()
和MyView.java
中的相仿
下一步, 找到rd = request.getRequestDispatcher(dispatcherPath);
下一步
指定重定向流程-Debug源码
debug源码
SpringMVC有很多视图种类
下一步
进入
进入, 调用RedirectView
的视图渲染方法
课后作业
1.熟悉前面的SpringMVC
映射数据请求, 模型数据, 视图和视图解析的案例
2.清晰Debug源码
的流程
3.完成一个简单的用户登录案例
1)编写登陆页面web路径/homework/login.jsp
2)com.zzw.web.homework.LoginHandler [doLogin方法]
, 如果用户输入用户名是zzw
, 密码123
, 就可以登陆成功; 否则登陆失败
3)创建JavaBeancom.zzw.web.homework.entity.User.java
4)表单提交数据到doLogin
方法, 以User
对象形式接收
5)登陆成功到, 页面 web路径/WEB-INF/pages/homework/login_ok.jsp
, 并显示登陆欢迎信息 [显示用户名, 模型数据会自动填充到request域中]
6)登陆失败到, 页面 web路径/WEB-INF/pages/homework/login_err.jsp
, 并给出重新登录的超链接 [考察web工程路径应用问题]
代码实现
1.在web路径/homework
下 新建login.jsp
<head>
<title>登陆页面</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/doLogin">
用户名: <input type="text" name="username"/><br/><br/>
密 码: <input type="text" name="pwd"/><br/><br/>
<input type="submit" value="登录"/>
</form>
</body>
2.在com.zzw.web.homework
下新建LoginHandler
@Controller
public class LoginHandler {
@RequestMapping("/doLogin")
public String doLogin(User user) {
System.out.println("===========doLogin()===========");
if ("zzw".equals(user.getUsername()) && "123".equals(user.getPwd())) {
//使用的是默认视图解析器
return "forward:/WEB-INF/pages/homework/login_ok.jsp";
} else {
return "forward:/WEB-INF/pages/homework/login_err.jsp";
}
}
}
3.填充login.jsp
的请求路径 涉及-web工程路径问题
<%--
<%=request.getContextPath()%>/doLogin => /springmvc/doLogin
/springmvc/doLogin 被浏览器解析成 http://localhost:8080/springmvc/doLogin
--%>
<form action="<%=request.getContextPath()%>/doLogin">
4.在com.zzw.web.homework.entity
下新建User.java
public class User {
private String username;
private String pwd;
//setter, getter方法
}
5.在web路径/WEB-INF/pages/homework
下 新建login_ok.jsp
登录成功到login_ok.jsp
, 并显示登陆欢迎信息 模型数据默认放入request域.
<head>
<title>登陆成功</title>
</head>
<body>
登陆成功, 欢迎 ${requestScope.user.username}
</body>
6.在web路径/WEB-INF/pages/homework
下 新建login_err.jsp
登陆失败到login_err.jsp
, 并给出重新登陆的超链接. 相对路径的知识点
<head>
<title>登陆失败</title>
</head>
<body>
<a href="homework/login.jsp">重新登陆</a>
</body>
使用自定义视图解析器再次完成
代码实现
1.springDispatcherServlet-servlet.xml
中注释掉默认视图解析器
的优先级, 使用自定义视图
<!--<property name="order" value="10"/>-->
2.修改LoginHandler
@Controller
public class LoginHandler {
@RequestMapping("/doLogin")
public String doLogin(User user) {
System.out.println("===========doLogin()===========");
return "homeworkView";
}
}
3.新建com.zzw.web.homework.viewresolver.MyView
/**
* 解读
* 1.MyView 继承了AbstractView, 就可以作为一个试图使用
* 2.@Component("homeworkView") 表示该视图会注入到容器中, 名字/id 是 homeworkView
*/
@Component("homeworkView")
public class MyView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//完成视图渲染
//并且可以确定我们要跳转的页面
System.out.println("进入到了自己的视图");
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
if ("zzw".equals(username) && "123".equals(pwd)) {
request.setAttribute("username", username);
request.getRequestDispatcher("/WEB-INF/pages/homework/login_ok.jsp")
.forward(request, response);
} else {
request.getRequestDispatcher("/WEB-INF/pages/homework/login_err.jsp")
.forward(request, response);
}
}
}
4.修改login_ok.jsp
登陆成功, 欢迎 ${requestScope.username}
下一讲, 我们学习 SpringMVC系列七: 手动实现SpringMVC底层机制-上