SpringMVC返回视图常见的 5 种方式,你会几种?| SpringMVC系列第7篇

大家好,我是【路人甲 Java】号主路人,本文如果对你有帮助,点个在看,顺便忙转发一下,非常需要大家的支持,对 java 有兴趣的朋友欢迎加我微信 itsoku 交流。

当 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

<table border="1" cellpadding="10" cellspacing="0">
    <tr>
        <th width="50">id</th>
        <th width="100">name</th>
        <th width="50">age</th>
    </tr>
    <c:forEach items="${userList}" var="user">
        <tr align="center">
            <td>${user.id}</td>
            <td>${user.name}</td>
            <td>${user.age}</td>
        </tr>
    </c:forEach>
</table>

使用 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.jspuser/add
/WEB-INF/view/user/list.jspuser/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 来帮你执行重定向操作。

10、SpringMVC 系列

  1. SpringMVC 系列第 1 篇:helloword

  2. SpringMVC 系列第 2 篇:@Controller、@RequestMapping

  3. SpringMVC 系列第 3 篇:异常高效的一款接口测试利器

  4. SpringMVC 系列第 4 篇:controller 常见的接收参数的方式

  5. SpringMVC 系列第 5 篇:@RequestBody 大解密,说点你不知道的

  6. SpringMVC 系列第 6 篇:上传文件的 4 种方式,你都会么?

11、更多好文章

  1. Spring 高手系列(共 56 篇)

  2. Java 高并发系列(共 34 篇)

  3. MySql 高手系列(共 27 篇)

  4. Maven 高手系列(共 10 篇)

  5. Mybatis 系列(共 12 篇)

  6. 聊聊 db 和缓存一致性常见的实现方式

  7. 接口幂等性这么重要,它是什么?怎么实现?

  8. 泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!

12、推荐一个高质量的公众号

大家平时在学习技术的过程中,苦于找不到高质量的学习资料的,可以关注一下【Java 充电社】,这个号专注于为大家提供高质量的学习资源,已发布了大量高质量的学习视频、及资源,大家可以关注下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路人甲Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值