Spring WebMvc基础-页面数据传递与请求参数绑定

从 0 开始深入学习 Spring 专栏目录总览

OK ,准备好了基本的工程环境,接下来我们先来搞定第一个需求:加载部门列表,以及按部门名称的条件查询。

1. 加载部门列表——页面数据传递【掌握】

如果小伙伴已经将 GitHub 仓库中的前端页面都扒下来了,那么在 /pages/dept 下有一个 deptList.jsp 的页面,它就是展示部门列表的。把他复制到自己练习的工程下就可以了。

1.1 需求描述

加载部门列表这个蛮简单的,跳转到部门列表页,并且展示数据库中的部门数据。

1.2 jsp编码

在 deptList.jsp 中,已经给各位预留了一个 deptList 的参数,在中间的 c:forEach 标签中循环加载:

<table id="dept-table" border="1">
    <thead>
    <tr>
        <th width="320px">id</th>
        <th width="150px">名称</th>
        <th width="150px">电话</th>
        <th width="100px">操作</th>
    </tr>
    </thead>
    <tbody>
    <c:if test="${deptList != null}">
        <c:forEach items="${deptList}" var="dept">
            <tr>
                <td align="center">${dept.id}</td>
                <td align="center">${dept.name}</td>
                <td align="center">${dept.tel}</td>
                <td align="center">
                    <a href="${pageContext.request.contextPath}/department/edit?id=${dept.id}">编辑</a>
                    <a href="javascript:del(${dept.id})">删除</a>
                </td>
            </tr>
        </c:forEach>
    </c:if>
    </tbody>
</table>

1.3 Controller页面跳转

既然有了页面,那接下来就是让 Controller 去跳转这个页面了。我们来到 DepartmentController 中,来写一个新的方法:

    @RequestMapping("/department/list")
    public String list() {
        return "dept/deptList";
    }

这样写完之后,就可以进行页面跳转了。

部署应用,启动 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list ,可以发现页面可以成功跳转:

Spring WebMvc基础-页面数据传递与请求参数绑定

不要吐槽样式丑啦。。。我这不还得照顾刚学完 JavaWeb 的小伙伴嘛。。。小册寻思着用那些复杂的 UI 框架啥的,会在一定程度上分散小伙伴的注意力,倒不如直接用最最基本的东西来展示,以强调我们要学习的重点。

但是数据呢。。。怎么把数据交给页面呢?

1.4 将部门数据传递给页面

小册先不着急讲,小伙伴们先回忆一下之前在 JavaWeb 中你们是怎么实现的。

1.4.1 基于request域的数据传递

之前在 JavaWeb 中,我们学习过,将 Servlet 中的数据传给页面,只需要把数据放入 request 域即可。所以在这里我们也把查询到的部门数据放入 request 域就可以了。

可是。。。怎么拿到那个 HttpServletRequest 呢?其实非常简单,直接在方法的参数列表上声明 HttpServletRequest 就可以了:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        
        return "dept/deptList";
    }

是不是很惊讶!这样写就可以了!甚至你还可以直接将 HttpServletRequest 当做一个成员属性,定义在 Controller 中:

@Controller
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    HttpServletRequest request;
    
    @RequestMapping("/department/list")
    public String list() {
        
        return "dept/deptList";
    }

不用担心这样写会有什么问题,SpringWebMvc 会帮我们注入的,我们尽管拿来用。

那接下来就是我们熟悉的东西了,向 HttpServletRequest 中放入查询的结果:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        request.setAttribute("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

OK 写完之后,重启 Tomcat ,刷新浏览器,可以发现部门数据成功查询到,并返回显示到了页面上:

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.2 基于ModelAndView的数据传递

除了上面最最基础的借助 HttpServletRequest ,SpringWebMvc 还给我们提供了一些其他的方案。比较原始的方案是借助一个 ModelAndView 的对象。下面我们来介绍它的使用。

为了不跟上面的方法产生冲突,咱另定义一个 list2 方法,并将请求路径也改为 /department/list2 。

使用 ModelAndView 的方法,也是在请求方法的参数列表上挂上一个 ModelAndView 对象,并且方法的返回值也是这个 ModelAndView :

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        
        return mav;
    }

见名知意,这个对象中肯定能封装数据和返回跳转的视图咯,所以我们就可以往其中塞入查询到的部门数据,以及跳转的 deptList.jsp 页面:

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

当然,也可以不在方法参数中声明 ModelAndView ,自己 new 的也可以:

    @RequestMapping("/department/list2")
    public ModelAndView list2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

这样写完之后,重启 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list2 ,可以发现依然可以正常跳转,并展示部门数据。

这个 ModelAndView 的使用非常简单,能塞数据,能设置视图跳转和响应状态码,仅此而已,小伙伴们自己用两下自然就熟悉了。

Spring WebMvc基础-页面数据传递与请求参数绑定

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.3 基于ModelMap的数据传递

除了上面的 ModelAndView 之外,我们还可以把这两种方法结合起来,形成第三种方法:把模型封装到方法参数的 Model 上,并返回 View 。怎么理解呢?上面咱不是写了可以直接返回视图的名称嘛,只是把数据直接放入了 request 域中,那我们还可以在方法参数上挂一个 Model 或者 ModelMap 或者 Map

    @RequestMapping("/department/list3")
    public String list3(ModelMap map) { // 可换成Model、Map<String, Object>
        map.put("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

看,这样写之后,代码复杂度跟第一种直接放入 request 域中相仿,但实现的最终效果是一样的。我们可以重启 Tomcat ,并重新访问 http://localhost:8080/spring-webmvc/department/list3 ,可以发现仍然可以正常跳转,说明这种套路也是可以的。

三种方法,小伙伴们可根据自己的喜好进行选择,或者在实际的项目开发中,与项目整体的风格保持一致。

2. 部门名称模糊查询——请求参数绑定【掌握】

接下来,部门信息都展示出来了,下面我们来实现部门名称的模糊查询。

2.1 需求描述

部门列表的上面有一个部门名称的输入框,输入部门名称,可以实现模糊查询。

2.2 jsp编码

在 jsp 页面上,已经给小伙伴们留下查询框的表单了:

<div>
    <form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
        <label>部门名称:</label>
        <input type="text" name="name" value="">
        <input type="submit" value="查询">
    </form>
</div>

2.3 Controller收集参数

那既然页面都有了参数了,接下来就是如何把参数收集过来了。对于 Controller 中的参数收集,有如下几种办法。

2.3.1 基于request的参数收集

其实不学 SpringWebMvc 你也应该知道,很简单嘛,用 request.getParameter 方法就可以取到请求参数:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        String name = request.getParameter("name");
        // service中已经提供了基于name的模糊查询方法
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这是最原始的方法,有没有 SpringWebMvc 都一样。

2.3.2 基于原生数据类型的参数收集

既然咱用了框架,那框架自然会帮我们想到这一点。所以我们可以直接在 Controller 的方法上,声明 name :

    @RequestMapping("/department/list")
    public String list(String name) {
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这样写是不是简单太多了?好,咱重启一下 Tomcat ,试一下效果。

Tomcat 重启后,在部门名称中输入 ha ,点击查询,发现确实只查到了那个 haha :

Spring WebMvc基础-页面数据传递与请求参数绑定

注意浏览器中也确实把 ha 带进去了,说明这种方案也是完全 OK 的。

2.3.3 基于模型类的参数收集

除了使用原生的数据类型之外,如果请求的参数名称,与模型类中一一对应,则可以直接使用模型类来接收,就像这样:

    @RequestMapping("/department/list")
    public String list(Department dept) {
        // service中已经提供了基于模型类的模糊查询
        request.setAttribute("deptList", departmentService.findDepartments(dept);
        return "dept/deptList";
    }

这样写完之后,我们只需要把整个 dept 扔给 DepartmentService ,DepartmentService 转给 DepartmetDao 后就可以自己整合里面的字段,并生成 SQL 语句,这些都是咱预先写好的了。重点是,这个 dept 中能不能把 name 收集过来。

能不能,检验一下就 OK 了,重启 Tomcat ,直接刷新浏览器,可以发现模糊查询依然生效,Debug 也可以发现 dept 中确实有了值:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4 参数绑定的重要注解@RequestParam

在基本的参数绑定中,有一个非常重要的注解:@RequestParam ,下面咱讲解一下它的使用。

2.4.1 参数名称的指定

实际开发中可能会出现一些例外情况,比方说页面上传的参数不叫 name ,而叫 dept_name :

<form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
    <label>部门名称:</label>
    <!-- 这里换name了!!!!! -->
    <input type="text" name="dept_name" value="">
    <input type="submit" value="查询">
</form>

这种情况下用 name 去接收就不好使了,因为属性名与请求的参数名没有能对的上的,所以就无法封装。这个时候就需要 @RequestParam 注解出马了,可以通过标注在指定的属性上,并声明 value 为参数的名称,这样就可以保证封装的参数名,与 Controller 参数的名称形成对应了。

使用方式很简单,只需要将 @RequestParam 标注在对应的方法参数即可:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request, @RequestParam(value = "dept_name") String name) { ... }

这样即便是再重启 Tomcat ,再测试发送请求,也不会出问题了:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4.2 参数是否必传

注意,如果此时还是传入 name 进入,会响应 400 错误:

Spring WebMvc基础-页面数据传递与请求参数绑定

这是因为 @RequestParam 注解中有一个属性:required ,它表示请求参数中是否必须携带指定的参数。它的默认值是 true ,代表必须携带。所以才出现上面的 dept_name 参数缺失导致 400 的现象。

解决它的方法很简单,把 required 改为 false ,就可以正常查询了。只不过因为没有传 dept_name ,所以 name 拿到的是 null ,也就不会有过滤的效果了。

2.4.3 参数的默认值

对于某些场景,比方说分页吧,分页的请求可以设置不传每页大小,这个时候就需要一个默认值。@RequestParam 注解就有一个属性 defaultValue ,它可以指定参数不传递时的默认值。

比方说我们有这样一个 Controller 的方法:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, Integer pageSize) {
        // 分页查询,存入request域
        return "dept/deptList";
    }

如果不传入 pageSize 的话,默认每页查询 10 条,那就可以这样写:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { ... }

这样便可以实现默认值的赋值。

OK ,准备好了基本的工程环境,接下来我们先来搞定第一个需求:加载部门列表,以及按部门名称的条件查询。

1. 加载部门列表——页面数据传递【掌握】

如果小伙伴已经将 GitHub 仓库中的前端页面都扒下来了,那么在 /pages/dept 下有一个 deptList.jsp 的页面,它就是展示部门列表的。把他复制到自己练习的工程下就可以了。

1.1 需求描述

加载部门列表这个蛮简单的,跳转到部门列表页,并且展示数据库中的部门数据。

1.2 jsp编码

在 deptList.jsp 中,已经给各位预留了一个 deptList 的参数,在中间的 c:forEach 标签中循环加载:

<table id="dept-table" border="1">
    <thead>
    <tr>
        <th width="320px">id</th>
        <th width="150px">名称</th>
        <th width="150px">电话</th>
        <th width="100px">操作</th>
    </tr>
    </thead>
    <tbody>
    <c:if test="${deptList != null}">
        <c:forEach items="${deptList}" var="dept">
            <tr>
                <td align="center">${dept.id}</td>
                <td align="center">${dept.name}</td>
                <td align="center">${dept.tel}</td>
                <td align="center">
                    <a href="${pageContext.request.contextPath}/department/edit?id=${dept.id}">编辑</a>
                    <a href="javascript:del(${dept.id})">删除</a>
                </td>
            </tr>
        </c:forEach>
    </c:if>
    </tbody>
</table>

1.3 Controller页面跳转

既然有了页面,那接下来就是让 Controller 去跳转这个页面了。我们来到 DepartmentController 中,来写一个新的方法:

    @RequestMapping("/department/list")
    public String list() {
        return "dept/deptList";
    }

这样写完之后,就可以进行页面跳转了。

部署应用,启动 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list ,可以发现页面可以成功跳转:

Spring WebMvc基础-页面数据传递与请求参数绑定

不要吐槽样式丑啦。。。我这不还得照顾刚学完 JavaWeb 的小伙伴嘛。。。小册寻思着用那些复杂的 UI 框架啥的,会在一定程度上分散小伙伴的注意力,倒不如直接用最最基本的东西来展示,以强调我们要学习的重点。

但是数据呢。。。怎么把数据交给页面呢?

1.4 将部门数据传递给页面

小册先不着急讲,小伙伴们先回忆一下之前在 JavaWeb 中你们是怎么实现的。

1.4.1 基于request域的数据传递

之前在 JavaWeb 中,我们学习过,将 Servlet 中的数据传给页面,只需要把数据放入 request 域即可。所以在这里我们也把查询到的部门数据放入 request 域就可以了。

可是。。。怎么拿到那个 HttpServletRequest 呢?其实非常简单,直接在方法的参数列表上声明 HttpServletRequest 就可以了:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        
        return "dept/deptList";
    }

是不是很惊讶!这样写就可以了!甚至你还可以直接将 HttpServletRequest 当做一个成员属性,定义在 Controller 中:

@Controller
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    HttpServletRequest request;
    
    @RequestMapping("/department/list")
    public String list() {
        
        return "dept/deptList";
    }

不用担心这样写会有什么问题,SpringWebMvc 会帮我们注入的,我们尽管拿来用。

那接下来就是我们熟悉的东西了,向 HttpServletRequest 中放入查询的结果:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        request.setAttribute("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

OK 写完之后,重启 Tomcat ,刷新浏览器,可以发现部门数据成功查询到,并返回显示到了页面上:

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.2 基于ModelAndView的数据传递

除了上面最最基础的借助 HttpServletRequest ,SpringWebMvc 还给我们提供了一些其他的方案。比较原始的方案是借助一个 ModelAndView 的对象。下面我们来介绍它的使用。

为了不跟上面的方法产生冲突,咱另定义一个 list2 方法,并将请求路径也改为 /department/list2 。

使用 ModelAndView 的方法,也是在请求方法的参数列表上挂上一个 ModelAndView 对象,并且方法的返回值也是这个 ModelAndView :

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        
        return mav;
    }

见名知意,这个对象中肯定能封装数据和返回跳转的视图咯,所以我们就可以往其中塞入查询到的部门数据,以及跳转的 deptList.jsp 页面:

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

当然,也可以不在方法参数中声明 ModelAndView ,自己 new 的也可以:

    @RequestMapping("/department/list2")
    public ModelAndView list2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

这样写完之后,重启 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list2 ,可以发现依然可以正常跳转,并展示部门数据。

这个 ModelAndView 的使用非常简单,能塞数据,能设置视图跳转和响应状态码,仅此而已,小伙伴们自己用两下自然就熟悉了。

Spring WebMvc基础-页面数据传递与请求参数绑定

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.3 基于ModelMap的数据传递

除了上面的 ModelAndView 之外,我们还可以把这两种方法结合起来,形成第三种方法:把模型封装到方法参数的 Model 上,并返回 View 。怎么理解呢?上面咱不是写了可以直接返回视图的名称嘛,只是把数据直接放入了 request 域中,那我们还可以在方法参数上挂一个 Model 或者 ModelMap 或者 Map

    @RequestMapping("/department/list3")
    public String list3(ModelMap map) { // 可换成Model、Map<String, Object>
        map.put("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

看,这样写之后,代码复杂度跟第一种直接放入 request 域中相仿,但实现的最终效果是一样的。我们可以重启 Tomcat ,并重新访问 http://localhost:8080/spring-webmvc/department/list3 ,可以发现仍然可以正常跳转,说明这种套路也是可以的。

三种方法,小伙伴们可根据自己的喜好进行选择,或者在实际的项目开发中,与项目整体的风格保持一致。

2. 部门名称模糊查询——请求参数绑定【掌握】

接下来,部门信息都展示出来了,下面我们来实现部门名称的模糊查询。

2.1 需求描述

部门列表的上面有一个部门名称的输入框,输入部门名称,可以实现模糊查询。

2.2 jsp编码

在 jsp 页面上,已经给小伙伴们留下查询框的表单了:

<div>
    <form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
        <label>部门名称:</label>
        <input type="text" name="name" value="">
        <input type="submit" value="查询">
    </form>
</div>

2.3 Controller收集参数

那既然页面都有了参数了,接下来就是如何把参数收集过来了。对于 Controller 中的参数收集,有如下几种办法。

2.3.1 基于request的参数收集

其实不学 SpringWebMvc 你也应该知道,很简单嘛,用 request.getParameter 方法就可以取到请求参数:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        String name = request.getParameter("name");
        // service中已经提供了基于name的模糊查询方法
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这是最原始的方法,有没有 SpringWebMvc 都一样。

2.3.2 基于原生数据类型的参数收集

既然咱用了框架,那框架自然会帮我们想到这一点。所以我们可以直接在 Controller 的方法上,声明 name :

    @RequestMapping("/department/list")
    public String list(String name) {
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这样写是不是简单太多了?好,咱重启一下 Tomcat ,试一下效果。

Tomcat 重启后,在部门名称中输入 ha ,点击查询,发现确实只查到了那个 haha :

Spring WebMvc基础-页面数据传递与请求参数绑定

注意浏览器中也确实把 ha 带进去了,说明这种方案也是完全 OK 的。

2.3.3 基于模型类的参数收集

除了使用原生的数据类型之外,如果请求的参数名称,与模型类中一一对应,则可以直接使用模型类来接收,就像这样:

    @RequestMapping("/department/list")
    public String list(Department dept) {
        // service中已经提供了基于模型类的模糊查询
        request.setAttribute("deptList", departmentService.findDepartments(dept);
        return "dept/deptList";
    }

这样写完之后,我们只需要把整个 dept 扔给 DepartmentService ,DepartmentService 转给 DepartmetDao 后就可以自己整合里面的字段,并生成 SQL 语句,这些都是咱预先写好的了。重点是,这个 dept 中能不能把 name 收集过来。

能不能,检验一下就 OK 了,重启 Tomcat ,直接刷新浏览器,可以发现模糊查询依然生效,Debug 也可以发现 dept 中确实有了值:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4 参数绑定的重要注解@RequestParam

在基本的参数绑定中,有一个非常重要的注解:@RequestParam ,下面咱讲解一下它的使用。

2.4.1 参数名称的指定

实际开发中可能会出现一些例外情况,比方说页面上传的参数不叫 name ,而叫 dept_name :

<form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
    <label>部门名称:</label>
    <!-- 这里换name了!!!!! -->
    <input type="text" name="dept_name" value="">
    <input type="submit" value="查询">
</form>

这种情况下用 name 去接收就不好使了,因为属性名与请求的参数名没有能对的上的,所以就无法封装。这个时候就需要 @RequestParam 注解出马了,可以通过标注在指定的属性上,并声明 value 为参数的名称,这样就可以保证封装的参数名,与 Controller 参数的名称形成对应了。

使用方式很简单,只需要将 @RequestParam 标注在对应的方法参数即可:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request, @RequestParam(value = "dept_name") String name) { ... }

这样即便是再重启 Tomcat ,再测试发送请求,也不会出问题了:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4.2 参数是否必传

注意,如果此时还是传入 name 进入,会响应 400 错误:

Spring WebMvc基础-页面数据传递与请求参数绑定

这是因为 @RequestParam 注解中有一个属性:required ,它表示请求参数中是否必须携带指定的参数。它的默认值是 true ,代表必须携带。所以才出现上面的 dept_name 参数缺失导致 400 的现象。

解决它的方法很简单,把 required 改为 false ,就可以正常查询了。只不过因为没有传 dept_name ,所以 name 拿到的是 null ,也就不会有过滤的效果了。

2.4.3 参数的默认值

对于某些场景,比方说分页吧,分页的请求可以设置不传每页大小,这个时候就需要一个默认值。@RequestParam 注解就有一个属性 defaultValue ,它可以指定参数不传递时的默认值。

比方说我们有这样一个 Controller 的方法:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, Integer pageSize) {
        // 分页查询,存入request域
        return "dept/deptList";
    }

如果不传入 pageSize 的话,默认每页查询 10 条,那就可以这样写:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { ... }

这样便可以实现默认值的赋值。

OK ,准备好了基本的工程环境,接下来我们先来搞定第一个需求:加载部门列表,以及按部门名称的条件查询。

1. 加载部门列表——页面数据传递【掌握】

如果小伙伴已经将 GitHub 仓库中的前端页面都扒下来了,那么在 /pages/dept 下有一个 deptList.jsp 的页面,它就是展示部门列表的。把他复制到自己练习的工程下就可以了。

1.1 需求描述

加载部门列表这个蛮简单的,跳转到部门列表页,并且展示数据库中的部门数据。

1.2 jsp编码

在 deptList.jsp 中,已经给各位预留了一个 deptList 的参数,在中间的 c:forEach 标签中循环加载:

<table id="dept-table" border="1">
    <thead>
    <tr>
        <th width="320px">id</th>
        <th width="150px">名称</th>
        <th width="150px">电话</th>
        <th width="100px">操作</th>
    </tr>
    </thead>
    <tbody>
    <c:if test="${deptList != null}">
        <c:forEach items="${deptList}" var="dept">
            <tr>
                <td align="center">${dept.id}</td>
                <td align="center">${dept.name}</td>
                <td align="center">${dept.tel}</td>
                <td align="center">
                    <a href="${pageContext.request.contextPath}/department/edit?id=${dept.id}">编辑</a>
                    <a href="javascript:del(${dept.id})">删除</a>
                </td>
            </tr>
        </c:forEach>
    </c:if>
    </tbody>
</table>

1.3 Controller页面跳转

既然有了页面,那接下来就是让 Controller 去跳转这个页面了。我们来到 DepartmentController 中,来写一个新的方法:

    @RequestMapping("/department/list")
    public String list() {
        return "dept/deptList";
    }

这样写完之后,就可以进行页面跳转了。

部署应用,启动 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list ,可以发现页面可以成功跳转:

Spring WebMvc基础-页面数据传递与请求参数绑定

不要吐槽样式丑啦。。。我这不还得照顾刚学完 JavaWeb 的小伙伴嘛。。。小册寻思着用那些复杂的 UI 框架啥的,会在一定程度上分散小伙伴的注意力,倒不如直接用最最基本的东西来展示,以强调我们要学习的重点。

但是数据呢。。。怎么把数据交给页面呢?

1.4 将部门数据传递给页面

小册先不着急讲,小伙伴们先回忆一下之前在 JavaWeb 中你们是怎么实现的。

1.4.1 基于request域的数据传递

之前在 JavaWeb 中,我们学习过,将 Servlet 中的数据传给页面,只需要把数据放入 request 域即可。所以在这里我们也把查询到的部门数据放入 request 域就可以了。

可是。。。怎么拿到那个 HttpServletRequest 呢?其实非常简单,直接在方法的参数列表上声明 HttpServletRequest 就可以了:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        
        return "dept/deptList";
    }

是不是很惊讶!这样写就可以了!甚至你还可以直接将 HttpServletRequest 当做一个成员属性,定义在 Controller 中:

@Controller
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    HttpServletRequest request;
    
    @RequestMapping("/department/list")
    public String list() {
        
        return "dept/deptList";
    }

不用担心这样写会有什么问题,SpringWebMvc 会帮我们注入的,我们尽管拿来用。

那接下来就是我们熟悉的东西了,向 HttpServletRequest 中放入查询的结果:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        request.setAttribute("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

OK 写完之后,重启 Tomcat ,刷新浏览器,可以发现部门数据成功查询到,并返回显示到了页面上:

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.2 基于ModelAndView的数据传递

除了上面最最基础的借助 HttpServletRequest ,SpringWebMvc 还给我们提供了一些其他的方案。比较原始的方案是借助一个 ModelAndView 的对象。下面我们来介绍它的使用。

为了不跟上面的方法产生冲突,咱另定义一个 list2 方法,并将请求路径也改为 /department/list2 。

使用 ModelAndView 的方法,也是在请求方法的参数列表上挂上一个 ModelAndView 对象,并且方法的返回值也是这个 ModelAndView :

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        
        return mav;
    }

见名知意,这个对象中肯定能封装数据和返回跳转的视图咯,所以我们就可以往其中塞入查询到的部门数据,以及跳转的 deptList.jsp 页面:

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

当然,也可以不在方法参数中声明 ModelAndView ,自己 new 的也可以:

    @RequestMapping("/department/list2")
    public ModelAndView list2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

这样写完之后,重启 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list2 ,可以发现依然可以正常跳转,并展示部门数据。

这个 ModelAndView 的使用非常简单,能塞数据,能设置视图跳转和响应状态码,仅此而已,小伙伴们自己用两下自然就熟悉了。

Spring WebMvc基础-页面数据传递与请求参数绑定

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.3 基于ModelMap的数据传递

除了上面的 ModelAndView 之外,我们还可以把这两种方法结合起来,形成第三种方法:把模型封装到方法参数的 Model 上,并返回 View 。怎么理解呢?上面咱不是写了可以直接返回视图的名称嘛,只是把数据直接放入了 request 域中,那我们还可以在方法参数上挂一个 Model 或者 ModelMap 或者 Map

    @RequestMapping("/department/list3")
    public String list3(ModelMap map) { // 可换成Model、Map<String, Object>
        map.put("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

看,这样写之后,代码复杂度跟第一种直接放入 request 域中相仿,但实现的最终效果是一样的。我们可以重启 Tomcat ,并重新访问 http://localhost:8080/spring-webmvc/department/list3 ,可以发现仍然可以正常跳转,说明这种套路也是可以的。

三种方法,小伙伴们可根据自己的喜好进行选择,或者在实际的项目开发中,与项目整体的风格保持一致。

2. 部门名称模糊查询——请求参数绑定【掌握】

接下来,部门信息都展示出来了,下面我们来实现部门名称的模糊查询。

2.1 需求描述

部门列表的上面有一个部门名称的输入框,输入部门名称,可以实现模糊查询。

2.2 jsp编码

在 jsp 页面上,已经给小伙伴们留下查询框的表单了:

<div>
    <form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
        <label>部门名称:</label>
        <input type="text" name="name" value="">
        <input type="submit" value="查询">
    </form>
</div>

2.3 Controller收集参数

那既然页面都有了参数了,接下来就是如何把参数收集过来了。对于 Controller 中的参数收集,有如下几种办法。

2.3.1 基于request的参数收集

其实不学 SpringWebMvc 你也应该知道,很简单嘛,用 request.getParameter 方法就可以取到请求参数:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        String name = request.getParameter("name");
        // service中已经提供了基于name的模糊查询方法
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这是最原始的方法,有没有 SpringWebMvc 都一样。

2.3.2 基于原生数据类型的参数收集

既然咱用了框架,那框架自然会帮我们想到这一点。所以我们可以直接在 Controller 的方法上,声明 name :

    @RequestMapping("/department/list")
    public String list(String name) {
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这样写是不是简单太多了?好,咱重启一下 Tomcat ,试一下效果。

Tomcat 重启后,在部门名称中输入 ha ,点击查询,发现确实只查到了那个 haha :

Spring WebMvc基础-页面数据传递与请求参数绑定

注意浏览器中也确实把 ha 带进去了,说明这种方案也是完全 OK 的。

2.3.3 基于模型类的参数收集

除了使用原生的数据类型之外,如果请求的参数名称,与模型类中一一对应,则可以直接使用模型类来接收,就像这样:

    @RequestMapping("/department/list")
    public String list(Department dept) {
        // service中已经提供了基于模型类的模糊查询
        request.setAttribute("deptList", departmentService.findDepartments(dept);
        return "dept/deptList";
    }

这样写完之后,我们只需要把整个 dept 扔给 DepartmentService ,DepartmentService 转给 DepartmetDao 后就可以自己整合里面的字段,并生成 SQL 语句,这些都是咱预先写好的了。重点是,这个 dept 中能不能把 name 收集过来。

能不能,检验一下就 OK 了,重启 Tomcat ,直接刷新浏览器,可以发现模糊查询依然生效,Debug 也可以发现 dept 中确实有了值:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4 参数绑定的重要注解@RequestParam

在基本的参数绑定中,有一个非常重要的注解:@RequestParam ,下面咱讲解一下它的使用。

2.4.1 参数名称的指定

实际开发中可能会出现一些例外情况,比方说页面上传的参数不叫 name ,而叫 dept_name :

<form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
    <label>部门名称:</label>
    <!-- 这里换name了!!!!! -->
    <input type="text" name="dept_name" value="">
    <input type="submit" value="查询">
</form>

这种情况下用 name 去接收就不好使了,因为属性名与请求的参数名没有能对的上的,所以就无法封装。这个时候就需要 @RequestParam 注解出马了,可以通过标注在指定的属性上,并声明 value 为参数的名称,这样就可以保证封装的参数名,与 Controller 参数的名称形成对应了。

使用方式很简单,只需要将 @RequestParam 标注在对应的方法参数即可:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request, @RequestParam(value = "dept_name") String name) { ... }

这样即便是再重启 Tomcat ,再测试发送请求,也不会出问题了:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4.2 参数是否必传

注意,如果此时还是传入 name 进入,会响应 400 错误:

Spring WebMvc基础-页面数据传递与请求参数绑定

这是因为 @RequestParam 注解中有一个属性:required ,它表示请求参数中是否必须携带指定的参数。它的默认值是 true ,代表必须携带。所以才出现上面的 dept_name 参数缺失导致 400 的现象。

解决它的方法很简单,把 required 改为 false ,就可以正常查询了。只不过因为没有传 dept_name ,所以 name 拿到的是 null ,也就不会有过滤的效果了。

2.4.3 参数的默认值

对于某些场景,比方说分页吧,分页的请求可以设置不传每页大小,这个时候就需要一个默认值。@RequestParam 注解就有一个属性 defaultValue ,它可以指定参数不传递时的默认值。

比方说我们有这样一个 Controller 的方法:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, Integer pageSize) {
        // 分页查询,存入request域
        return "dept/deptList";
    }

如果不传入 pageSize 的话,默认每页查询 10 条,那就可以这样写:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { ... }

这样便可以实现默认值的赋值。

OK ,准备好了基本的工程环境,接下来我们先来搞定第一个需求:加载部门列表,以及按部门名称的条件查询。

1. 加载部门列表——页面数据传递【掌握】

如果小伙伴已经将 GitHub 仓库中的前端页面都扒下来了,那么在 /pages/dept 下有一个 deptList.jsp 的页面,它就是展示部门列表的。把他复制到自己练习的工程下就可以了。

1.1 需求描述

加载部门列表这个蛮简单的,跳转到部门列表页,并且展示数据库中的部门数据。

1.2 jsp编码

在 deptList.jsp 中,已经给各位预留了一个 deptList 的参数,在中间的 c:forEach 标签中循环加载:

<table id="dept-table" border="1">
    <thead>
    <tr>
        <th width="320px">id</th>
        <th width="150px">名称</th>
        <th width="150px">电话</th>
        <th width="100px">操作</th>
    </tr>
    </thead>
    <tbody>
    <c:if test="${deptList != null}">
        <c:forEach items="${deptList}" var="dept">
            <tr>
                <td align="center">${dept.id}</td>
                <td align="center">${dept.name}</td>
                <td align="center">${dept.tel}</td>
                <td align="center">
                    <a href="${pageContext.request.contextPath}/department/edit?id=${dept.id}">编辑</a>
                    <a href="javascript:del(${dept.id})">删除</a>
                </td>
            </tr>
        </c:forEach>
    </c:if>
    </tbody>
</table>

1.3 Controller页面跳转

既然有了页面,那接下来就是让 Controller 去跳转这个页面了。我们来到 DepartmentController 中,来写一个新的方法:

    @RequestMapping("/department/list")
    public String list() {
        return "dept/deptList";
    }

这样写完之后,就可以进行页面跳转了。

部署应用,启动 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list ,可以发现页面可以成功跳转:

Spring WebMvc基础-页面数据传递与请求参数绑定

不要吐槽样式丑啦。。。我这不还得照顾刚学完 JavaWeb 的小伙伴嘛。。。小册寻思着用那些复杂的 UI 框架啥的,会在一定程度上分散小伙伴的注意力,倒不如直接用最最基本的东西来展示,以强调我们要学习的重点。

但是数据呢。。。怎么把数据交给页面呢?

1.4 将部门数据传递给页面

小册先不着急讲,小伙伴们先回忆一下之前在 JavaWeb 中你们是怎么实现的。

1.4.1 基于request域的数据传递

之前在 JavaWeb 中,我们学习过,将 Servlet 中的数据传给页面,只需要把数据放入 request 域即可。所以在这里我们也把查询到的部门数据放入 request 域就可以了。

可是。。。怎么拿到那个 HttpServletRequest 呢?其实非常简单,直接在方法的参数列表上声明 HttpServletRequest 就可以了:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        
        return "dept/deptList";
    }

是不是很惊讶!这样写就可以了!甚至你还可以直接将 HttpServletRequest 当做一个成员属性,定义在 Controller 中:

@Controller
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    HttpServletRequest request;
    
    @RequestMapping("/department/list")
    public String list() {
        
        return "dept/deptList";
    }

不用担心这样写会有什么问题,SpringWebMvc 会帮我们注入的,我们尽管拿来用。

那接下来就是我们熟悉的东西了,向 HttpServletRequest 中放入查询的结果:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        request.setAttribute("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

OK 写完之后,重启 Tomcat ,刷新浏览器,可以发现部门数据成功查询到,并返回显示到了页面上:

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.2 基于ModelAndView的数据传递

除了上面最最基础的借助 HttpServletRequest ,SpringWebMvc 还给我们提供了一些其他的方案。比较原始的方案是借助一个 ModelAndView 的对象。下面我们来介绍它的使用。

为了不跟上面的方法产生冲突,咱另定义一个 list2 方法,并将请求路径也改为 /department/list2 。

使用 ModelAndView 的方法,也是在请求方法的参数列表上挂上一个 ModelAndView 对象,并且方法的返回值也是这个 ModelAndView :

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        
        return mav;
    }

见名知意,这个对象中肯定能封装数据和返回跳转的视图咯,所以我们就可以往其中塞入查询到的部门数据,以及跳转的 deptList.jsp 页面:

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

当然,也可以不在方法参数中声明 ModelAndView ,自己 new 的也可以:

    @RequestMapping("/department/list2")
    public ModelAndView list2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

这样写完之后,重启 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list2 ,可以发现依然可以正常跳转,并展示部门数据。

这个 ModelAndView 的使用非常简单,能塞数据,能设置视图跳转和响应状态码,仅此而已,小伙伴们自己用两下自然就熟悉了。

Spring WebMvc基础-页面数据传递与请求参数绑定

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.3 基于ModelMap的数据传递

除了上面的 ModelAndView 之外,我们还可以把这两种方法结合起来,形成第三种方法:把模型封装到方法参数的 Model 上,并返回 View 。怎么理解呢?上面咱不是写了可以直接返回视图的名称嘛,只是把数据直接放入了 request 域中,那我们还可以在方法参数上挂一个 Model 或者 ModelMap 或者 Map

    @RequestMapping("/department/list3")
    public String list3(ModelMap map) { // 可换成Model、Map<String, Object>
        map.put("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

看,这样写之后,代码复杂度跟第一种直接放入 request 域中相仿,但实现的最终效果是一样的。我们可以重启 Tomcat ,并重新访问 http://localhost:8080/spring-webmvc/department/list3 ,可以发现仍然可以正常跳转,说明这种套路也是可以的。

三种方法,小伙伴们可根据自己的喜好进行选择,或者在实际的项目开发中,与项目整体的风格保持一致。

2. 部门名称模糊查询——请求参数绑定【掌握】

接下来,部门信息都展示出来了,下面我们来实现部门名称的模糊查询。

2.1 需求描述

部门列表的上面有一个部门名称的输入框,输入部门名称,可以实现模糊查询。

2.2 jsp编码

在 jsp 页面上,已经给小伙伴们留下查询框的表单了:

<div>
    <form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
        <label>部门名称:</label>
        <input type="text" name="name" value="">
        <input type="submit" value="查询">
    </form>
</div>

2.3 Controller收集参数

那既然页面都有了参数了,接下来就是如何把参数收集过来了。对于 Controller 中的参数收集,有如下几种办法。

2.3.1 基于request的参数收集

其实不学 SpringWebMvc 你也应该知道,很简单嘛,用 request.getParameter 方法就可以取到请求参数:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        String name = request.getParameter("name");
        // service中已经提供了基于name的模糊查询方法
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这是最原始的方法,有没有 SpringWebMvc 都一样。

2.3.2 基于原生数据类型的参数收集

既然咱用了框架,那框架自然会帮我们想到这一点。所以我们可以直接在 Controller 的方法上,声明 name :

    @RequestMapping("/department/list")
    public String list(String name) {
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这样写是不是简单太多了?好,咱重启一下 Tomcat ,试一下效果。

Tomcat 重启后,在部门名称中输入 ha ,点击查询,发现确实只查到了那个 haha :

Spring WebMvc基础-页面数据传递与请求参数绑定

注意浏览器中也确实把 ha 带进去了,说明这种方案也是完全 OK 的。

2.3.3 基于模型类的参数收集

除了使用原生的数据类型之外,如果请求的参数名称,与模型类中一一对应,则可以直接使用模型类来接收,就像这样:

    @RequestMapping("/department/list")
    public String list(Department dept) {
        // service中已经提供了基于模型类的模糊查询
        request.setAttribute("deptList", departmentService.findDepartments(dept);
        return "dept/deptList";
    }

这样写完之后,我们只需要把整个 dept 扔给 DepartmentService ,DepartmentService 转给 DepartmetDao 后就可以自己整合里面的字段,并生成 SQL 语句,这些都是咱预先写好的了。重点是,这个 dept 中能不能把 name 收集过来。

能不能,检验一下就 OK 了,重启 Tomcat ,直接刷新浏览器,可以发现模糊查询依然生效,Debug 也可以发现 dept 中确实有了值:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4 参数绑定的重要注解@RequestParam

在基本的参数绑定中,有一个非常重要的注解:@RequestParam ,下面咱讲解一下它的使用。

2.4.1 参数名称的指定

实际开发中可能会出现一些例外情况,比方说页面上传的参数不叫 name ,而叫 dept_name :

<form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
    <label>部门名称:</label>
    <!-- 这里换name了!!!!! -->
    <input type="text" name="dept_name" value="">
    <input type="submit" value="查询">
</form>

这种情况下用 name 去接收就不好使了,因为属性名与请求的参数名没有能对的上的,所以就无法封装。这个时候就需要 @RequestParam 注解出马了,可以通过标注在指定的属性上,并声明 value 为参数的名称,这样就可以保证封装的参数名,与 Controller 参数的名称形成对应了。

使用方式很简单,只需要将 @RequestParam 标注在对应的方法参数即可:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request, @RequestParam(value = "dept_name") String name) { ... }

这样即便是再重启 Tomcat ,再测试发送请求,也不会出问题了:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4.2 参数是否必传

注意,如果此时还是传入 name 进入,会响应 400 错误:

Spring WebMvc基础-页面数据传递与请求参数绑定

这是因为 @RequestParam 注解中有一个属性:required ,它表示请求参数中是否必须携带指定的参数。它的默认值是 true ,代表必须携带。所以才出现上面的 dept_name 参数缺失导致 400 的现象。

解决它的方法很简单,把 required 改为 false ,就可以正常查询了。只不过因为没有传 dept_name ,所以 name 拿到的是 null ,也就不会有过滤的效果了。

2.4.3 参数的默认值

对于某些场景,比方说分页吧,分页的请求可以设置不传每页大小,这个时候就需要一个默认值。@RequestParam 注解就有一个属性 defaultValue ,它可以指定参数不传递时的默认值。

比方说我们有这样一个 Controller 的方法:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, Integer pageSize) {
        // 分页查询,存入request域
        return "dept/deptList";
    }

如果不传入 pageSize 的话,默认每页查询 10 条,那就可以这样写:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { ... }

这样便可以实现默认值的赋值。

OK ,准备好了基本的工程环境,接下来我们先来搞定第一个需求:加载部门列表,以及按部门名称的条件查询。

1. 加载部门列表——页面数据传递【掌握】

如果小伙伴已经将 GitHub 仓库中的前端页面都扒下来了,那么在 /pages/dept 下有一个 deptList.jsp 的页面,它就是展示部门列表的。把他复制到自己练习的工程下就可以了。

1.1 需求描述

加载部门列表这个蛮简单的,跳转到部门列表页,并且展示数据库中的部门数据。

1.2 jsp编码

在 deptList.jsp 中,已经给各位预留了一个 deptList 的参数,在中间的 c:forEach 标签中循环加载:

<table id="dept-table" border="1">
    <thead>
    <tr>
        <th width="320px">id</th>
        <th width="150px">名称</th>
        <th width="150px">电话</th>
        <th width="100px">操作</th>
    </tr>
    </thead>
    <tbody>
    <c:if test="${deptList != null}">
        <c:forEach items="${deptList}" var="dept">
            <tr>
                <td align="center">${dept.id}</td>
                <td align="center">${dept.name}</td>
                <td align="center">${dept.tel}</td>
                <td align="center">
                    <a href="${pageContext.request.contextPath}/department/edit?id=${dept.id}">编辑</a>
                    <a href="javascript:del(${dept.id})">删除</a>
                </td>
            </tr>
        </c:forEach>
    </c:if>
    </tbody>
</table>

1.3 Controller页面跳转

既然有了页面,那接下来就是让 Controller 去跳转这个页面了。我们来到 DepartmentController 中,来写一个新的方法:

    @RequestMapping("/department/list")
    public String list() {
        return "dept/deptList";
    }

这样写完之后,就可以进行页面跳转了。

部署应用,启动 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list ,可以发现页面可以成功跳转:

Spring WebMvc基础-页面数据传递与请求参数绑定

不要吐槽样式丑啦。。。我这不还得照顾刚学完 JavaWeb 的小伙伴嘛。。。小册寻思着用那些复杂的 UI 框架啥的,会在一定程度上分散小伙伴的注意力,倒不如直接用最最基本的东西来展示,以强调我们要学习的重点。

但是数据呢。。。怎么把数据交给页面呢?

1.4 将部门数据传递给页面

小册先不着急讲,小伙伴们先回忆一下之前在 JavaWeb 中你们是怎么实现的。

1.4.1 基于request域的数据传递

之前在 JavaWeb 中,我们学习过,将 Servlet 中的数据传给页面,只需要把数据放入 request 域即可。所以在这里我们也把查询到的部门数据放入 request 域就可以了。

可是。。。怎么拿到那个 HttpServletRequest 呢?其实非常简单,直接在方法的参数列表上声明 HttpServletRequest 就可以了:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        
        return "dept/deptList";
    }

是不是很惊讶!这样写就可以了!甚至你还可以直接将 HttpServletRequest 当做一个成员属性,定义在 Controller 中:

@Controller
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    HttpServletRequest request;
    
    @RequestMapping("/department/list")
    public String list() {
        
        return "dept/deptList";
    }

不用担心这样写会有什么问题,SpringWebMvc 会帮我们注入的,我们尽管拿来用。

那接下来就是我们熟悉的东西了,向 HttpServletRequest 中放入查询的结果:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        request.setAttribute("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

OK 写完之后,重启 Tomcat ,刷新浏览器,可以发现部门数据成功查询到,并返回显示到了页面上:

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.2 基于ModelAndView的数据传递

除了上面最最基础的借助 HttpServletRequest ,SpringWebMvc 还给我们提供了一些其他的方案。比较原始的方案是借助一个 ModelAndView 的对象。下面我们来介绍它的使用。

为了不跟上面的方法产生冲突,咱另定义一个 list2 方法,并将请求路径也改为 /department/list2 。

使用 ModelAndView 的方法,也是在请求方法的参数列表上挂上一个 ModelAndView 对象,并且方法的返回值也是这个 ModelAndView :

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        
        return mav;
    }

见名知意,这个对象中肯定能封装数据和返回跳转的视图咯,所以我们就可以往其中塞入查询到的部门数据,以及跳转的 deptList.jsp 页面:

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

当然,也可以不在方法参数中声明 ModelAndView ,自己 new 的也可以:

    @RequestMapping("/department/list2")
    public ModelAndView list2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

这样写完之后,重启 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list2 ,可以发现依然可以正常跳转,并展示部门数据。

这个 ModelAndView 的使用非常简单,能塞数据,能设置视图跳转和响应状态码,仅此而已,小伙伴们自己用两下自然就熟悉了。

Spring WebMvc基础-页面数据传递与请求参数绑定

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.3 基于ModelMap的数据传递

除了上面的 ModelAndView 之外,我们还可以把这两种方法结合起来,形成第三种方法:把模型封装到方法参数的 Model 上,并返回 View 。怎么理解呢?上面咱不是写了可以直接返回视图的名称嘛,只是把数据直接放入了 request 域中,那我们还可以在方法参数上挂一个 Model 或者 ModelMap 或者 Map

    @RequestMapping("/department/list3")
    public String list3(ModelMap map) { // 可换成Model、Map<String, Object>
        map.put("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

看,这样写之后,代码复杂度跟第一种直接放入 request 域中相仿,但实现的最终效果是一样的。我们可以重启 Tomcat ,并重新访问 http://localhost:8080/spring-webmvc/department/list3 ,可以发现仍然可以正常跳转,说明这种套路也是可以的。

三种方法,小伙伴们可根据自己的喜好进行选择,或者在实际的项目开发中,与项目整体的风格保持一致。

2. 部门名称模糊查询——请求参数绑定【掌握】

接下来,部门信息都展示出来了,下面我们来实现部门名称的模糊查询。

2.1 需求描述

部门列表的上面有一个部门名称的输入框,输入部门名称,可以实现模糊查询。

2.2 jsp编码

在 jsp 页面上,已经给小伙伴们留下查询框的表单了:

<div>
    <form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
        <label>部门名称:</label>
        <input type="text" name="name" value="">
        <input type="submit" value="查询">
    </form>
</div>

2.3 Controller收集参数

那既然页面都有了参数了,接下来就是如何把参数收集过来了。对于 Controller 中的参数收集,有如下几种办法。

2.3.1 基于request的参数收集

其实不学 SpringWebMvc 你也应该知道,很简单嘛,用 request.getParameter 方法就可以取到请求参数:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        String name = request.getParameter("name");
        // service中已经提供了基于name的模糊查询方法
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这是最原始的方法,有没有 SpringWebMvc 都一样。

2.3.2 基于原生数据类型的参数收集

既然咱用了框架,那框架自然会帮我们想到这一点。所以我们可以直接在 Controller 的方法上,声明 name :

    @RequestMapping("/department/list")
    public String list(String name) {
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这样写是不是简单太多了?好,咱重启一下 Tomcat ,试一下效果。

Tomcat 重启后,在部门名称中输入 ha ,点击查询,发现确实只查到了那个 haha :

Spring WebMvc基础-页面数据传递与请求参数绑定

注意浏览器中也确实把 ha 带进去了,说明这种方案也是完全 OK 的。

2.3.3 基于模型类的参数收集

除了使用原生的数据类型之外,如果请求的参数名称,与模型类中一一对应,则可以直接使用模型类来接收,就像这样:

    @RequestMapping("/department/list")
    public String list(Department dept) {
        // service中已经提供了基于模型类的模糊查询
        request.setAttribute("deptList", departmentService.findDepartments(dept);
        return "dept/deptList";
    }

这样写完之后,我们只需要把整个 dept 扔给 DepartmentService ,DepartmentService 转给 DepartmetDao 后就可以自己整合里面的字段,并生成 SQL 语句,这些都是咱预先写好的了。重点是,这个 dept 中能不能把 name 收集过来。

能不能,检验一下就 OK 了,重启 Tomcat ,直接刷新浏览器,可以发现模糊查询依然生效,Debug 也可以发现 dept 中确实有了值:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4 参数绑定的重要注解@RequestParam

在基本的参数绑定中,有一个非常重要的注解:@RequestParam ,下面咱讲解一下它的使用。

2.4.1 参数名称的指定

实际开发中可能会出现一些例外情况,比方说页面上传的参数不叫 name ,而叫 dept_name :

<form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
    <label>部门名称:</label>
    <!-- 这里换name了!!!!! -->
    <input type="text" name="dept_name" value="">
    <input type="submit" value="查询">
</form>

这种情况下用 name 去接收就不好使了,因为属性名与请求的参数名没有能对的上的,所以就无法封装。这个时候就需要 @RequestParam 注解出马了,可以通过标注在指定的属性上,并声明 value 为参数的名称,这样就可以保证封装的参数名,与 Controller 参数的名称形成对应了。

使用方式很简单,只需要将 @RequestParam 标注在对应的方法参数即可:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request, @RequestParam(value = "dept_name") String name) { ... }

这样即便是再重启 Tomcat ,再测试发送请求,也不会出问题了:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4.2 参数是否必传

注意,如果此时还是传入 name 进入,会响应 400 错误:

Spring WebMvc基础-页面数据传递与请求参数绑定

这是因为 @RequestParam 注解中有一个属性:required ,它表示请求参数中是否必须携带指定的参数。它的默认值是 true ,代表必须携带。所以才出现上面的 dept_name 参数缺失导致 400 的现象。

解决它的方法很简单,把 required 改为 false ,就可以正常查询了。只不过因为没有传 dept_name ,所以 name 拿到的是 null ,也就不会有过滤的效果了。

2.4.3 参数的默认值

对于某些场景,比方说分页吧,分页的请求可以设置不传每页大小,这个时候就需要一个默认值。@RequestParam 注解就有一个属性 defaultValue ,它可以指定参数不传递时的默认值。

比方说我们有这样一个 Controller 的方法:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, Integer pageSize) {
        // 分页查询,存入request域
        return "dept/deptList";
    }

如果不传入 pageSize 的话,默认每页查询 10 条,那就可以这样写:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { ... }

这样便可以实现默认值的赋值。

OK ,准备好了基本的工程环境,接下来我们先来搞定第一个需求:加载部门列表,以及按部门名称的条件查询。

1. 加载部门列表——页面数据传递【掌握】

如果小伙伴已经将 GitHub 仓库中的前端页面都扒下来了,那么在 /pages/dept 下有一个 deptList.jsp 的页面,它就是展示部门列表的。把他复制到自己练习的工程下就可以了。

1.1 需求描述

加载部门列表这个蛮简单的,跳转到部门列表页,并且展示数据库中的部门数据。

1.2 jsp编码

在 deptList.jsp 中,已经给各位预留了一个 deptList 的参数,在中间的 c:forEach 标签中循环加载:

<table id="dept-table" border="1">
    <thead>
    <tr>
        <th width="320px">id</th>
        <th width="150px">名称</th>
        <th width="150px">电话</th>
        <th width="100px">操作</th>
    </tr>
    </thead>
    <tbody>
    <c:if test="${deptList != null}">
        <c:forEach items="${deptList}" var="dept">
            <tr>
                <td align="center">${dept.id}</td>
                <td align="center">${dept.name}</td>
                <td align="center">${dept.tel}</td>
                <td align="center">
                    <a href="${pageContext.request.contextPath}/department/edit?id=${dept.id}">编辑</a>
                    <a href="javascript:del(${dept.id})">删除</a>
                </td>
            </tr>
        </c:forEach>
    </c:if>
    </tbody>
</table>

1.3 Controller页面跳转

既然有了页面,那接下来就是让 Controller 去跳转这个页面了。我们来到 DepartmentController 中,来写一个新的方法:

    @RequestMapping("/department/list")
    public String list() {
        return "dept/deptList";
    }

这样写完之后,就可以进行页面跳转了。

部署应用,启动 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list ,可以发现页面可以成功跳转:

Spring WebMvc基础-页面数据传递与请求参数绑定

不要吐槽样式丑啦。。。我这不还得照顾刚学完 JavaWeb 的小伙伴嘛。。。小册寻思着用那些复杂的 UI 框架啥的,会在一定程度上分散小伙伴的注意力,倒不如直接用最最基本的东西来展示,以强调我们要学习的重点。

但是数据呢。。。怎么把数据交给页面呢?

1.4 将部门数据传递给页面

小册先不着急讲,小伙伴们先回忆一下之前在 JavaWeb 中你们是怎么实现的。

1.4.1 基于request域的数据传递

之前在 JavaWeb 中,我们学习过,将 Servlet 中的数据传给页面,只需要把数据放入 request 域即可。所以在这里我们也把查询到的部门数据放入 request 域就可以了。

可是。。。怎么拿到那个 HttpServletRequest 呢?其实非常简单,直接在方法的参数列表上声明 HttpServletRequest 就可以了:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        
        return "dept/deptList";
    }

是不是很惊讶!这样写就可以了!甚至你还可以直接将 HttpServletRequest 当做一个成员属性,定义在 Controller 中:

@Controller
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    HttpServletRequest request;
    
    @RequestMapping("/department/list")
    public String list() {
        
        return "dept/deptList";
    }

不用担心这样写会有什么问题,SpringWebMvc 会帮我们注入的,我们尽管拿来用。

那接下来就是我们熟悉的东西了,向 HttpServletRequest 中放入查询的结果:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        request.setAttribute("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

OK 写完之后,重启 Tomcat ,刷新浏览器,可以发现部门数据成功查询到,并返回显示到了页面上:

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.2 基于ModelAndView的数据传递

除了上面最最基础的借助 HttpServletRequest ,SpringWebMvc 还给我们提供了一些其他的方案。比较原始的方案是借助一个 ModelAndView 的对象。下面我们来介绍它的使用。

为了不跟上面的方法产生冲突,咱另定义一个 list2 方法,并将请求路径也改为 /department/list2 。

使用 ModelAndView 的方法,也是在请求方法的参数列表上挂上一个 ModelAndView 对象,并且方法的返回值也是这个 ModelAndView :

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        
        return mav;
    }

见名知意,这个对象中肯定能封装数据和返回跳转的视图咯,所以我们就可以往其中塞入查询到的部门数据,以及跳转的 deptList.jsp 页面:

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

当然,也可以不在方法参数中声明 ModelAndView ,自己 new 的也可以:

    @RequestMapping("/department/list2")
    public ModelAndView list2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

这样写完之后,重启 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list2 ,可以发现依然可以正常跳转,并展示部门数据。

这个 ModelAndView 的使用非常简单,能塞数据,能设置视图跳转和响应状态码,仅此而已,小伙伴们自己用两下自然就熟悉了。

Spring WebMvc基础-页面数据传递与请求参数绑定

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.3 基于ModelMap的数据传递

除了上面的 ModelAndView 之外,我们还可以把这两种方法结合起来,形成第三种方法:把模型封装到方法参数的 Model 上,并返回 View 。怎么理解呢?上面咱不是写了可以直接返回视图的名称嘛,只是把数据直接放入了 request 域中,那我们还可以在方法参数上挂一个 Model 或者 ModelMap 或者 Map

    @RequestMapping("/department/list3")
    public String list3(ModelMap map) { // 可换成Model、Map<String, Object>
        map.put("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

看,这样写之后,代码复杂度跟第一种直接放入 request 域中相仿,但实现的最终效果是一样的。我们可以重启 Tomcat ,并重新访问 http://localhost:8080/spring-webmvc/department/list3 ,可以发现仍然可以正常跳转,说明这种套路也是可以的。

三种方法,小伙伴们可根据自己的喜好进行选择,或者在实际的项目开发中,与项目整体的风格保持一致。

2. 部门名称模糊查询——请求参数绑定【掌握】

接下来,部门信息都展示出来了,下面我们来实现部门名称的模糊查询。

2.1 需求描述

部门列表的上面有一个部门名称的输入框,输入部门名称,可以实现模糊查询。

2.2 jsp编码

在 jsp 页面上,已经给小伙伴们留下查询框的表单了:

<div>
    <form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
        <label>部门名称:</label>
        <input type="text" name="name" value="">
        <input type="submit" value="查询">
    </form>
</div>

2.3 Controller收集参数

那既然页面都有了参数了,接下来就是如何把参数收集过来了。对于 Controller 中的参数收集,有如下几种办法。

2.3.1 基于request的参数收集

其实不学 SpringWebMvc 你也应该知道,很简单嘛,用 request.getParameter 方法就可以取到请求参数:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        String name = request.getParameter("name");
        // service中已经提供了基于name的模糊查询方法
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这是最原始的方法,有没有 SpringWebMvc 都一样。

2.3.2 基于原生数据类型的参数收集

既然咱用了框架,那框架自然会帮我们想到这一点。所以我们可以直接在 Controller 的方法上,声明 name :

    @RequestMapping("/department/list")
    public String list(String name) {
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这样写是不是简单太多了?好,咱重启一下 Tomcat ,试一下效果。

Tomcat 重启后,在部门名称中输入 ha ,点击查询,发现确实只查到了那个 haha :

Spring WebMvc基础-页面数据传递与请求参数绑定

注意浏览器中也确实把 ha 带进去了,说明这种方案也是完全 OK 的。

2.3.3 基于模型类的参数收集

除了使用原生的数据类型之外,如果请求的参数名称,与模型类中一一对应,则可以直接使用模型类来接收,就像这样:

    @RequestMapping("/department/list")
    public String list(Department dept) {
        // service中已经提供了基于模型类的模糊查询
        request.setAttribute("deptList", departmentService.findDepartments(dept);
        return "dept/deptList";
    }

这样写完之后,我们只需要把整个 dept 扔给 DepartmentService ,DepartmentService 转给 DepartmetDao 后就可以自己整合里面的字段,并生成 SQL 语句,这些都是咱预先写好的了。重点是,这个 dept 中能不能把 name 收集过来。

能不能,检验一下就 OK 了,重启 Tomcat ,直接刷新浏览器,可以发现模糊查询依然生效,Debug 也可以发现 dept 中确实有了值:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4 参数绑定的重要注解@RequestParam

在基本的参数绑定中,有一个非常重要的注解:@RequestParam ,下面咱讲解一下它的使用。

2.4.1 参数名称的指定

实际开发中可能会出现一些例外情况,比方说页面上传的参数不叫 name ,而叫 dept_name :

<form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
    <label>部门名称:</label>
    <!-- 这里换name了!!!!! -->
    <input type="text" name="dept_name" value="">
    <input type="submit" value="查询">
</form>

这种情况下用 name 去接收就不好使了,因为属性名与请求的参数名没有能对的上的,所以就无法封装。这个时候就需要 @RequestParam 注解出马了,可以通过标注在指定的属性上,并声明 value 为参数的名称,这样就可以保证封装的参数名,与 Controller 参数的名称形成对应了。

使用方式很简单,只需要将 @RequestParam 标注在对应的方法参数即可:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request, @RequestParam(value = "dept_name") String name) { ... }

这样即便是再重启 Tomcat ,再测试发送请求,也不会出问题了:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4.2 参数是否必传

注意,如果此时还是传入 name 进入,会响应 400 错误:

Spring WebMvc基础-页面数据传递与请求参数绑定

这是因为 @RequestParam 注解中有一个属性:required ,它表示请求参数中是否必须携带指定的参数。它的默认值是 true ,代表必须携带。所以才出现上面的 dept_name 参数缺失导致 400 的现象。

解决它的方法很简单,把 required 改为 false ,就可以正常查询了。只不过因为没有传 dept_name ,所以 name 拿到的是 null ,也就不会有过滤的效果了。

2.4.3 参数的默认值

对于某些场景,比方说分页吧,分页的请求可以设置不传每页大小,这个时候就需要一个默认值。@RequestParam 注解就有一个属性 defaultValue ,它可以指定参数不传递时的默认值。

比方说我们有这样一个 Controller 的方法:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, Integer pageSize) {
        // 分页查询,存入request域
        return "dept/deptList";
    }

如果不传入 pageSize 的话,默认每页查询 10 条,那就可以这样写:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { ... }

这样便可以实现默认值的赋值。

OK ,准备好了基本的工程环境,接下来我们先来搞定第一个需求:加载部门列表,以及按部门名称的条件查询。

1. 加载部门列表——页面数据传递【掌握】

如果小伙伴已经将 GitHub 仓库中的前端页面都扒下来了,那么在 /pages/dept 下有一个 deptList.jsp 的页面,它就是展示部门列表的。把他复制到自己练习的工程下就可以了。

1.1 需求描述

加载部门列表这个蛮简单的,跳转到部门列表页,并且展示数据库中的部门数据。

1.2 jsp编码

在 deptList.jsp 中,已经给各位预留了一个 deptList 的参数,在中间的 c:forEach 标签中循环加载:

<table id="dept-table" border="1">
    <thead>
    <tr>
        <th width="320px">id</th>
        <th width="150px">名称</th>
        <th width="150px">电话</th>
        <th width="100px">操作</th>
    </tr>
    </thead>
    <tbody>
    <c:if test="${deptList != null}">
        <c:forEach items="${deptList}" var="dept">
            <tr>
                <td align="center">${dept.id}</td>
                <td align="center">${dept.name}</td>
                <td align="center">${dept.tel}</td>
                <td align="center">
                    <a href="${pageContext.request.contextPath}/department/edit?id=${dept.id}">编辑</a>
                    <a href="javascript:del(${dept.id})">删除</a>
                </td>
            </tr>
        </c:forEach>
    </c:if>
    </tbody>
</table>

1.3 Controller页面跳转

既然有了页面,那接下来就是让 Controller 去跳转这个页面了。我们来到 DepartmentController 中,来写一个新的方法:

    @RequestMapping("/department/list")
    public String list() {
        return "dept/deptList";
    }

这样写完之后,就可以进行页面跳转了。

部署应用,启动 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list ,可以发现页面可以成功跳转:

Spring WebMvc基础-页面数据传递与请求参数绑定

不要吐槽样式丑啦。。。我这不还得照顾刚学完 JavaWeb 的小伙伴嘛。。。小册寻思着用那些复杂的 UI 框架啥的,会在一定程度上分散小伙伴的注意力,倒不如直接用最最基本的东西来展示,以强调我们要学习的重点。

但是数据呢。。。怎么把数据交给页面呢?

1.4 将部门数据传递给页面

小册先不着急讲,小伙伴们先回忆一下之前在 JavaWeb 中你们是怎么实现的。

1.4.1 基于request域的数据传递

之前在 JavaWeb 中,我们学习过,将 Servlet 中的数据传给页面,只需要把数据放入 request 域即可。所以在这里我们也把查询到的部门数据放入 request 域就可以了。

可是。。。怎么拿到那个 HttpServletRequest 呢?其实非常简单,直接在方法的参数列表上声明 HttpServletRequest 就可以了:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        
        return "dept/deptList";
    }

是不是很惊讶!这样写就可以了!甚至你还可以直接将 HttpServletRequest 当做一个成员属性,定义在 Controller 中:

@Controller
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    HttpServletRequest request;
    
    @RequestMapping("/department/list")
    public String list() {
        
        return "dept/deptList";
    }

不用担心这样写会有什么问题,SpringWebMvc 会帮我们注入的,我们尽管拿来用。

那接下来就是我们熟悉的东西了,向 HttpServletRequest 中放入查询的结果:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        request.setAttribute("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

OK 写完之后,重启 Tomcat ,刷新浏览器,可以发现部门数据成功查询到,并返回显示到了页面上:

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.2 基于ModelAndView的数据传递

除了上面最最基础的借助 HttpServletRequest ,SpringWebMvc 还给我们提供了一些其他的方案。比较原始的方案是借助一个 ModelAndView 的对象。下面我们来介绍它的使用。

为了不跟上面的方法产生冲突,咱另定义一个 list2 方法,并将请求路径也改为 /department/list2 。

使用 ModelAndView 的方法,也是在请求方法的参数列表上挂上一个 ModelAndView 对象,并且方法的返回值也是这个 ModelAndView :

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        
        return mav;
    }

见名知意,这个对象中肯定能封装数据和返回跳转的视图咯,所以我们就可以往其中塞入查询到的部门数据,以及跳转的 deptList.jsp 页面:

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

当然,也可以不在方法参数中声明 ModelAndView ,自己 new 的也可以:

    @RequestMapping("/department/list2")
    public ModelAndView list2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

这样写完之后,重启 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list2 ,可以发现依然可以正常跳转,并展示部门数据。

这个 ModelAndView 的使用非常简单,能塞数据,能设置视图跳转和响应状态码,仅此而已,小伙伴们自己用两下自然就熟悉了。

Spring WebMvc基础-页面数据传递与请求参数绑定

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.3 基于ModelMap的数据传递

除了上面的 ModelAndView 之外,我们还可以把这两种方法结合起来,形成第三种方法:把模型封装到方法参数的 Model 上,并返回 View 。怎么理解呢?上面咱不是写了可以直接返回视图的名称嘛,只是把数据直接放入了 request 域中,那我们还可以在方法参数上挂一个 Model 或者 ModelMap 或者 Map

    @RequestMapping("/department/list3")
    public String list3(ModelMap map) { // 可换成Model、Map<String, Object>
        map.put("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

看,这样写之后,代码复杂度跟第一种直接放入 request 域中相仿,但实现的最终效果是一样的。我们可以重启 Tomcat ,并重新访问 http://localhost:8080/spring-webmvc/department/list3 ,可以发现仍然可以正常跳转,说明这种套路也是可以的。

三种方法,小伙伴们可根据自己的喜好进行选择,或者在实际的项目开发中,与项目整体的风格保持一致。

2. 部门名称模糊查询——请求参数绑定【掌握】

接下来,部门信息都展示出来了,下面我们来实现部门名称的模糊查询。

2.1 需求描述

部门列表的上面有一个部门名称的输入框,输入部门名称,可以实现模糊查询。

2.2 jsp编码

在 jsp 页面上,已经给小伙伴们留下查询框的表单了:

<div>
    <form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
        <label>部门名称:</label>
        <input type="text" name="name" value="">
        <input type="submit" value="查询">
    </form>
</div>

2.3 Controller收集参数

那既然页面都有了参数了,接下来就是如何把参数收集过来了。对于 Controller 中的参数收集,有如下几种办法。

2.3.1 基于request的参数收集

其实不学 SpringWebMvc 你也应该知道,很简单嘛,用 request.getParameter 方法就可以取到请求参数:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        String name = request.getParameter("name");
        // service中已经提供了基于name的模糊查询方法
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这是最原始的方法,有没有 SpringWebMvc 都一样。

2.3.2 基于原生数据类型的参数收集

既然咱用了框架,那框架自然会帮我们想到这一点。所以我们可以直接在 Controller 的方法上,声明 name :

    @RequestMapping("/department/list")
    public String list(String name) {
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这样写是不是简单太多了?好,咱重启一下 Tomcat ,试一下效果。

Tomcat 重启后,在部门名称中输入 ha ,点击查询,发现确实只查到了那个 haha :

Spring WebMvc基础-页面数据传递与请求参数绑定

注意浏览器中也确实把 ha 带进去了,说明这种方案也是完全 OK 的。

2.3.3 基于模型类的参数收集

除了使用原生的数据类型之外,如果请求的参数名称,与模型类中一一对应,则可以直接使用模型类来接收,就像这样:

    @RequestMapping("/department/list")
    public String list(Department dept) {
        // service中已经提供了基于模型类的模糊查询
        request.setAttribute("deptList", departmentService.findDepartments(dept);
        return "dept/deptList";
    }

这样写完之后,我们只需要把整个 dept 扔给 DepartmentService ,DepartmentService 转给 DepartmetDao 后就可以自己整合里面的字段,并生成 SQL 语句,这些都是咱预先写好的了。重点是,这个 dept 中能不能把 name 收集过来。

能不能,检验一下就 OK 了,重启 Tomcat ,直接刷新浏览器,可以发现模糊查询依然生效,Debug 也可以发现 dept 中确实有了值:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4 参数绑定的重要注解@RequestParam

在基本的参数绑定中,有一个非常重要的注解:@RequestParam ,下面咱讲解一下它的使用。

2.4.1 参数名称的指定

实际开发中可能会出现一些例外情况,比方说页面上传的参数不叫 name ,而叫 dept_name :

<form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
    <label>部门名称:</label>
    <!-- 这里换name了!!!!! -->
    <input type="text" name="dept_name" value="">
    <input type="submit" value="查询">
</form>

这种情况下用 name 去接收就不好使了,因为属性名与请求的参数名没有能对的上的,所以就无法封装。这个时候就需要 @RequestParam 注解出马了,可以通过标注在指定的属性上,并声明 value 为参数的名称,这样就可以保证封装的参数名,与 Controller 参数的名称形成对应了。

使用方式很简单,只需要将 @RequestParam 标注在对应的方法参数即可:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request, @RequestParam(value = "dept_name") String name) { ... }

这样即便是再重启 Tomcat ,再测试发送请求,也不会出问题了:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4.2 参数是否必传

注意,如果此时还是传入 name 进入,会响应 400 错误:

Spring WebMvc基础-页面数据传递与请求参数绑定

这是因为 @RequestParam 注解中有一个属性:required ,它表示请求参数中是否必须携带指定的参数。它的默认值是 true ,代表必须携带。所以才出现上面的 dept_name 参数缺失导致 400 的现象。

解决它的方法很简单,把 required 改为 false ,就可以正常查询了。只不过因为没有传 dept_name ,所以 name 拿到的是 null ,也就不会有过滤的效果了。

2.4.3 参数的默认值

对于某些场景,比方说分页吧,分页的请求可以设置不传每页大小,这个时候就需要一个默认值。@RequestParam 注解就有一个属性 defaultValue ,它可以指定参数不传递时的默认值。

比方说我们有这样一个 Controller 的方法:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, Integer pageSize) {
        // 分页查询,存入request域
        return "dept/deptList";
    }

如果不传入 pageSize 的话,默认每页查询 10 条,那就可以这样写:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { ... }

这样便可以实现默认值的赋值。

OK ,准备好了基本的工程环境,接下来我们先来搞定第一个需求:加载部门列表,以及按部门名称的条件查询。

1. 加载部门列表——页面数据传递【掌握】

如果小伙伴已经将 GitHub 仓库中的前端页面都扒下来了,那么在 /pages/dept 下有一个 deptList.jsp 的页面,它就是展示部门列表的。把他复制到自己练习的工程下就可以了。

1.1 需求描述

加载部门列表这个蛮简单的,跳转到部门列表页,并且展示数据库中的部门数据。

1.2 jsp编码

在 deptList.jsp 中,已经给各位预留了一个 deptList 的参数,在中间的 c:forEach 标签中循环加载:

<table id="dept-table" border="1">
    <thead>
    <tr>
        <th width="320px">id</th>
        <th width="150px">名称</th>
        <th width="150px">电话</th>
        <th width="100px">操作</th>
    </tr>
    </thead>
    <tbody>
    <c:if test="${deptList != null}">
        <c:forEach items="${deptList}" var="dept">
            <tr>
                <td align="center">${dept.id}</td>
                <td align="center">${dept.name}</td>
                <td align="center">${dept.tel}</td>
                <td align="center">
                    <a href="${pageContext.request.contextPath}/department/edit?id=${dept.id}">编辑</a>
                    <a href="javascript:del(${dept.id})">删除</a>
                </td>
            </tr>
        </c:forEach>
    </c:if>
    </tbody>
</table>

1.3 Controller页面跳转

既然有了页面,那接下来就是让 Controller 去跳转这个页面了。我们来到 DepartmentController 中,来写一个新的方法:

    @RequestMapping("/department/list")
    public String list() {
        return "dept/deptList";
    }

这样写完之后,就可以进行页面跳转了。

部署应用,启动 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list ,可以发现页面可以成功跳转:

Spring WebMvc基础-页面数据传递与请求参数绑定

不要吐槽样式丑啦。。。我这不还得照顾刚学完 JavaWeb 的小伙伴嘛。。。小册寻思着用那些复杂的 UI 框架啥的,会在一定程度上分散小伙伴的注意力,倒不如直接用最最基本的东西来展示,以强调我们要学习的重点。

但是数据呢。。。怎么把数据交给页面呢?

1.4 将部门数据传递给页面

小册先不着急讲,小伙伴们先回忆一下之前在 JavaWeb 中你们是怎么实现的。

1.4.1 基于request域的数据传递

之前在 JavaWeb 中,我们学习过,将 Servlet 中的数据传给页面,只需要把数据放入 request 域即可。所以在这里我们也把查询到的部门数据放入 request 域就可以了。

可是。。。怎么拿到那个 HttpServletRequest 呢?其实非常简单,直接在方法的参数列表上声明 HttpServletRequest 就可以了:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        
        return "dept/deptList";
    }

是不是很惊讶!这样写就可以了!甚至你还可以直接将 HttpServletRequest 当做一个成员属性,定义在 Controller 中:

@Controller
public class DepartmentController {
    
    @Autowired
    DepartmentService departmentService;
    
    @Autowired
    HttpServletRequest request;
    
    @RequestMapping("/department/list")
    public String list() {
        
        return "dept/deptList";
    }

不用担心这样写会有什么问题,SpringWebMvc 会帮我们注入的,我们尽管拿来用。

那接下来就是我们熟悉的东西了,向 HttpServletRequest 中放入查询的结果:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        request.setAttribute("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

OK 写完之后,重启 Tomcat ,刷新浏览器,可以发现部门数据成功查询到,并返回显示到了页面上:

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.2 基于ModelAndView的数据传递

除了上面最最基础的借助 HttpServletRequest ,SpringWebMvc 还给我们提供了一些其他的方案。比较原始的方案是借助一个 ModelAndView 的对象。下面我们来介绍它的使用。

为了不跟上面的方法产生冲突,咱另定义一个 list2 方法,并将请求路径也改为 /department/list2 。

使用 ModelAndView 的方法,也是在请求方法的参数列表上挂上一个 ModelAndView 对象,并且方法的返回值也是这个 ModelAndView :

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        
        return mav;
    }

见名知意,这个对象中肯定能封装数据和返回跳转的视图咯,所以我们就可以往其中塞入查询到的部门数据,以及跳转的 deptList.jsp 页面:

    @RequestMapping("/department/list2")
    public ModelAndView list2(ModelAndView mav) {
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

当然,也可以不在方法参数中声明 ModelAndView ,自己 new 的也可以:

    @RequestMapping("/department/list2")
    public ModelAndView list2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("deptList", departmentService.findDepartments(null));
        mav.setViewName("dept/deptList");
        return mav;
    }

这样写完之后,重启 Tomcat ,并访问 http://localhost:8080/spring-webmvc/department/list2 ,可以发现依然可以正常跳转,并展示部门数据。

这个 ModelAndView 的使用非常简单,能塞数据,能设置视图跳转和响应状态码,仅此而已,小伙伴们自己用两下自然就熟悉了。

Spring WebMvc基础-页面数据传递与请求参数绑定

Spring WebMvc基础-页面数据传递与请求参数绑定

1.4.3 基于ModelMap的数据传递

除了上面的 ModelAndView 之外,我们还可以把这两种方法结合起来,形成第三种方法:把模型封装到方法参数的 Model 上,并返回 View 。怎么理解呢?上面咱不是写了可以直接返回视图的名称嘛,只是把数据直接放入了 request 域中,那我们还可以在方法参数上挂一个 Model 或者 ModelMap 或者 Map

    @RequestMapping("/department/list3")
    public String list3(ModelMap map) { // 可换成Model、Map<String, Object>
        map.put("deptList", departmentService.findDepartments(null));
        return "dept/deptList";
    }

看,这样写之后,代码复杂度跟第一种直接放入 request 域中相仿,但实现的最终效果是一样的。我们可以重启 Tomcat ,并重新访问 http://localhost:8080/spring-webmvc/department/list3 ,可以发现仍然可以正常跳转,说明这种套路也是可以的。

三种方法,小伙伴们可根据自己的喜好进行选择,或者在实际的项目开发中,与项目整体的风格保持一致。

2. 部门名称模糊查询——请求参数绑定【掌握】

接下来,部门信息都展示出来了,下面我们来实现部门名称的模糊查询。

2.1 需求描述

部门列表的上面有一个部门名称的输入框,输入部门名称,可以实现模糊查询。

2.2 jsp编码

在 jsp 页面上,已经给小伙伴们留下查询框的表单了:

<div>
    <form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
        <label>部门名称:</label>
        <input type="text" name="name" value="">
        <input type="submit" value="查询">
    </form>
</div>

2.3 Controller收集参数

那既然页面都有了参数了,接下来就是如何把参数收集过来了。对于 Controller 中的参数收集,有如下几种办法。

2.3.1 基于request的参数收集

其实不学 SpringWebMvc 你也应该知道,很简单嘛,用 request.getParameter 方法就可以取到请求参数:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request) {
        String name = request.getParameter("name");
        // service中已经提供了基于name的模糊查询方法
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这是最原始的方法,有没有 SpringWebMvc 都一样。

2.3.2 基于原生数据类型的参数收集

既然咱用了框架,那框架自然会帮我们想到这一点。所以我们可以直接在 Controller 的方法上,声明 name :

    @RequestMapping("/department/list")
    public String list(String name) {
        request.setAttribute("deptList", departmentService.findDepartmentsByName(name));
        return "dept/deptList";
    }

这样写是不是简单太多了?好,咱重启一下 Tomcat ,试一下效果。

Tomcat 重启后,在部门名称中输入 ha ,点击查询,发现确实只查到了那个 haha :

Spring WebMvc基础-页面数据传递与请求参数绑定

注意浏览器中也确实把 ha 带进去了,说明这种方案也是完全 OK 的。

2.3.3 基于模型类的参数收集

除了使用原生的数据类型之外,如果请求的参数名称,与模型类中一一对应,则可以直接使用模型类来接收,就像这样:

    @RequestMapping("/department/list")
    public String list(Department dept) {
        // service中已经提供了基于模型类的模糊查询
        request.setAttribute("deptList", departmentService.findDepartments(dept);
        return "dept/deptList";
    }

这样写完之后,我们只需要把整个 dept 扔给 DepartmentService ,DepartmentService 转给 DepartmetDao 后就可以自己整合里面的字段,并生成 SQL 语句,这些都是咱预先写好的了。重点是,这个 dept 中能不能把 name 收集过来。

能不能,检验一下就 OK 了,重启 Tomcat ,直接刷新浏览器,可以发现模糊查询依然生效,Debug 也可以发现 dept 中确实有了值:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4 参数绑定的重要注解@RequestParam

在基本的参数绑定中,有一个非常重要的注解:@RequestParam ,下面咱讲解一下它的使用。

2.4.1 参数名称的指定

实际开发中可能会出现一些例外情况,比方说页面上传的参数不叫 name ,而叫 dept_name :

<form id="query-form" method="get" action="${pageContext.request.contextPath}/department/list">
    <label>部门名称:</label>
    <!-- 这里换name了!!!!! -->
    <input type="text" name="dept_name" value="">
    <input type="submit" value="查询">
</form>

这种情况下用 name 去接收就不好使了,因为属性名与请求的参数名没有能对的上的,所以就无法封装。这个时候就需要 @RequestParam 注解出马了,可以通过标注在指定的属性上,并声明 value 为参数的名称,这样就可以保证封装的参数名,与 Controller 参数的名称形成对应了。

使用方式很简单,只需要将 @RequestParam 标注在对应的方法参数即可:

    @RequestMapping("/department/list")
    public String list(HttpServletRequest request, @RequestParam(value = "dept_name") String name) { ... }

这样即便是再重启 Tomcat ,再测试发送请求,也不会出问题了:

Spring WebMvc基础-页面数据传递与请求参数绑定

2.4.2 参数是否必传

注意,如果此时还是传入 name 进入,会响应 400 错误:

Spring WebMvc基础-页面数据传递与请求参数绑定

这是因为 @RequestParam 注解中有一个属性:required ,它表示请求参数中是否必须携带指定的参数。它的默认值是 true ,代表必须携带。所以才出现上面的 dept_name 参数缺失导致 400 的现象。

解决它的方法很简单,把 required 改为 false ,就可以正常查询了。只不过因为没有传 dept_name ,所以 name 拿到的是 null ,也就不会有过滤的效果了。

2.4.3 参数的默认值

对于某些场景,比方说分页吧,分页的请求可以设置不传每页大小,这个时候就需要一个默认值。@RequestParam 注解就有一个属性 defaultValue ,它可以指定参数不传递时的默认值。

比方说我们有这样一个 Controller 的方法:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, Integer pageSize) {
        // 分页查询,存入request域
        return "dept/deptList";
    }

如果不传入 pageSize 的话,默认每页查询 10 条,那就可以这样写:

    @RequestMapping("/department/list")
    public String list(String name, Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { ... }

这样便可以实现默认值的赋值。

 

免责声明:
1.本站所有内容由本站原创、网络转载、消息撰写、网友投稿等几部分组成。
2.本站原创文字内容若未经特别声明,则遵循协议CC3.0共享协议,转载请务必注明原文链接。
3.本站部分来源于网络转载的文章信息是出于传递更多信息之目的,不意味着赞同其观点。
4.本站所有源码与软件均为原作者提供,仅供学习和研究使用。
5.如您对本网站的相关版权有任何异议,或者认为侵犯了您的合法权益,请及时通知我们处理。
火焰兔 » Spring WebMvc基础-页面数据传递与请求参数绑定