作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
学习必须往深处挖,挖的越深,基础越扎实!
阶段1、深入多线程
阶段2、深入多线程设计模式
阶段3、深入juc源码解析
码哥源码部分
码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】
码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】
码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】
码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】
打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】
当http请求被自定义的controller处理时,如何指定响应的页面呢?
这个就是我们本文需要讨论的问题。
在controller中响应页面有很多种方式,稍后我们会一一介绍,大家需要掌握每种方式的用法以及这些方式之间的区别,以后能够灵活使用。
本文用到的页面我都以jsp为例,其他页面模板技术,比如freemarker、velocity、thymeleaf、enjoy,这些我们后面专门再开篇讲解。
1、本文内容
- SpringMVC 返回页面的3种方式及区别
- SpringMVC 重定向的2种方式及区别
2、软件版本
- idea 2020.3.3
- jdk1.8
- ≥maven3.6.1
- spring5.3.6
- apache-tomcat-9.0.46
3、先来回顾下servlet中响应页面的2种方式
SpringMVC底层是依靠servlet来实现的,所以我们先回顾下servlet中响应页面是如何实现的。
servlet中响应页面有2种常见的方式,而springmvc中通常也是依靠这2种方式实现的。
方式1:转向
request.getRequestDispatcher(path).forward(request,response);
1、path为转向的地址
2、发生在服务器端,浏览器的地址栏不会发生变化
3、path指定的页面,可以共享request请求中的数据
4、path必须是服务器端的资源
方式2:重定向
response.sendRedirect(location);
1、location为重定向的地址
2、重定向发生在客户端(浏览器端),所以会导致浏览器地址栏发生变化,变为location指定的地址
3、重定向会导致浏览器重新向服务器端发生一次请求,请求地址为location指定的地址
4、location可以为本服务器端的资源,也可以为外网可以访问的任意资源,比如:http://www.baidu.com
下面来详解springmvc中响应页面的5种方式。
4、方式1:返回ModelAndView
需求
通过springmvc显示用户列表,如下图
如何实现?
我们先来看一下如果用servlet是如何实现的,伪代码如下:
1、List<UserDto> userList = new ArrayList();
2、request.setAttribute("userList",userList);
3、request.getRequestDispatcher("/WEB-INF/view/user/list.jsp").forward(request,response);
关键代码就这几行,相当简单。
对应的jsp(/WEB-INF/view/user/list.jsp)关键代码如下,主要就是一个循环遍历用例列表userList,然后显示
| id|name|age|
| :-----: | :-----: | :-----: |
| ${user.id} | ${user.name} | ${user.age} |
使用springmvc实现
@Controller
public class UserController {
/**
* 用户列表(用户id->用户信息)
*/
Map<Long, UserDto> userDtoMap = new ConcurrentHashMap<>();
{
userDtoMap.put(1L, new UserDto(1L, "路人", 30));
userDtoMap.put(2L, new UserDto(2L, "张三", 20));
userDtoMap.put(3L, new UserDto(3L, "李四", 18));
}
/**
* 用户列表
*
* @return
*/
@RequestMapping("/user/list.do")
public ModelAndView list() {
//1.创建ModelAndView
ModelAndView modelAndView = new ModelAndView();
//2.将所有用户信息放到Model中
modelAndView.addObject("userList", userDtoMap.values());
//3.设置显示的页面
modelAndView.setViewName("/WEB-INF/view/user/list.jsp");
//4.返回ModelAndView
return modelAndView;
}
}
这里主要看list()这个方法,当调用这个方法的时候,最终效果和上面servlet的效果一样,这里用到了ModelAndView
。
ModelAndView:模型&视图
通常我们的页面都是动态的,客户端看到的页面,基本上都是模板(视图)+数据(数据模型),经过组装之后输出到客户端的。
所以响应客户端的请求,需要指定2个关键的信息:页面,页面中需要的数据。
springmvc中就使用ModelAndView来存放着2个关键的信息,通过modelAndView.addObject
方法来添加页面中需要用到的数据,通过modelAndView.setViewName("视图名称")
来设置需要显示的页面。
modelAndView.addObject("key","value")
添加页面中需要用到的数据,效果同:request.setAttribute("key","value");
modelAndView.setViewName("视图名称")
指定需要显示的视图命名,比如jsp地址
小结
如果页面中需要用到一些动态的数据,此时可以使用ModelAndView作为返回值,将动态数据放到ModelAndView中。
5、方式2:直接返回视图名称
当页面不需要用到后端的数据的时候,就只是显示一个页面,此时可以直接将视图的名称作为返回值就可以了,比如
/**
* 跳转到新增页面
*
* @return
*/
@RequestMapping("/user/add.do")
public String add() {
//直接返回视图的名称(页面的路径)
return "/WEB-INF/view/user/add.jsp";
}
6、方式3:指定视图解析器
存在的问题
大家看下上面2种方式,返回的视图名称,都以/WEB-INF/view/
开头,以.jsp
结尾对不对。
如果项目中我们规定所有的视图都符合这种规则,即都放在/WEB-INF/view/
目录中,都是jsp文件,那么我们可以将视图的名称是不是可以简化一下,怎么做的呢?
具体2个步骤。
step1:注册视图解析器
在springmvc配置文件中添加下面配置,来指定视图解析器。
这个bean会对视图的名称进行处理,有2个参数需要指定
- prefix:视图文件前缀
- suffix:视图文件后缀
最终视图的名称 = prefix+controller中指定的viewname+suffix
<!-- 添加视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
step2:调整controller中视图的名称
viewName旧值 | viewName新值 |
---|---|
/WEB-INF/view/user/add.jsp | user/add |
/WEB-INF/view/user/list.jsp | user/list |
@RequestMapping("/user/add.do")
public String add() {
//直接返回视图的名称(页面的路径)
return "user/add";
}
此时代码是不是简单多了。
7、SpringMVC实现重定向2种方式
需求
有时候,请求之后,需要做重定向操作,比如发送删除用户信息的请求/user/del/{用户id}.do
,后端处理成功之后,需重定向到用户列表页面/user/list.do
这里就需要用到重定向的操作了,在servlet的中对应代码是
response.sendRedirect(location);
springmvc中有好几种实现,这里我们主要掌握2种。
方式1:返回String类型
springmvc中实现重定向比较简单,视图的名称必须需要以redirect:
开头,比如下面代码,处理删除用户的请求,删除成功之后,重定向到用户列表页面
/**
* 删除用户信息,删除成功之后重定向到用户列表页
*
* @param userId 用户id
* @return
*/
@GetMapping("/user/del/{userId}.do")
public String del(@PathVariable("userId") Long userId, HttpServletRequest request) {
//删除用户信息
this.userDtoMap.remove(userId);
//重定向到用户列表页面,此时浏览器地址会发生变化,变为http://localhost:8080/chat05/user/list.do
return "redirect:/user/list.do";
}
方式2:返回ModelAndView类型
如果重定向的时候,我们需要向重定向的页面携带参数,一般我们可以这么做,代码如下:
return "redirect:/user/list.do?在这里拼参数";
比如
return "redirect:/user/list.do?p1=v1&p2=v2";
如果遇到了这种请求,参数比较少的情况,按照上面拼接是可以的。
springmvc中提供了更简单的方式,代码如下,最终springmv会指定将ModelAndView中添加的数据,拼接到重定向的url中
@GetMapping("/user/del1/{userId}.do")
public ModelAndView del1(@PathVariable("userId") Long userId) {
//删除用户记录
this.userDtoMap.remove(userId);
/**
* 重定向到用户列表页面,此时浏览器地址会发生变化,
* 变为http://localhost:8080/chat05/user/list.do?p1=v1&p2=v2
*/
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("p1", "v1");
modelAndView.addObject("p2", "v2");
modelAndView.setViewName("redirect:/user/list.do");
return modelAndView;
}
8、案例代码
git地址
https://gitee.com/javacode2018/springmvc-series
案例说明
案例中实现了用户信息的增删改查,用到了上面讲到的所有技术。
用户列表页
http://localhost:8080/chat05/user/list.do
新增用户页面
http://localhost:8080/chat05/user/add.do
修改用户信息页面
删除用户信息
删除用户信息之后,会被重定向到用户列表页,案例中列出了2种删除,用来模拟2种重定向的效果。
9、总结
- 响应页面通常有2种方式,第1种返回ModelAndView,这种方式比较适合页面中需要后端传递数据的,第2种方式直接返回视图的名称,这种适合无需传递数据的。
- springmvc容器中配置InternalResourceViewResolver视图解析器,用来简化controller中视图的名称
- 掌握重新的2种方式,重定向的关键点是视图名称要以
redirect:
开头,这样springmvc才知道你需要springmvc来帮你执行重定向操作。