springmvc概述(4)(不同对象[pojo/数组/list/map]参数绑定,校验,数据回显)
1 包装类型pojo参数绑定
ps:即参数对象的属性也是对象,将所有参数封装到一个对象中(对象的属性由多个对象属性组成)
使用场景:
1,当映射的方法需要多个参数对象的时候,
2,参数对象具有相同的属性,需要对相同属性赋值
不如将所有对象参数组合成一个类,统一进行处理
1.1 需求
商品查询controller方法中实现商品查询条件传入。
1.2 实现方法
第一种方法:
在形参中 添加HttpServletRequest request参数,通过request接收查询条件参数。
第二种方法:
在形参中让包装类型的pojo接收查询条件参数。
分析:
页面传参数的特点:复杂,多样性。条件包括 :用户账号、商品编号、订单信息。。。
如果将用户账号、商品编号、订单信息等放在简单pojo(属性是简单类型)中,pojo类属性比较多,比较乱。
建议使用包装类型的pojo,pojo中属性是pojo。
1.3 页面参数和controller方法形参定义
页面参数:
商品名称:<input name="itemsCustom.name" /> (ps:该名字对应是属性对象的属性) 注意:itemsCustom和包装pojo中的属性一致即可。
controller方法形参:
public ModelAndView queryItems(HttpServletRequest request,ItemsQueryVo itemsQueryVo) throws Exception

2 集合类型绑定
2.1 数组绑定
2.1.1 需求
商品批量删除,用户在页面选择多个商品,批量删除。
2.1.2 表现层实现
关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。
controller方法定义:

@Controller // 为了对url进行分类管理 ,可以在这里定义根路径,最终访问url是根路径+子路径 // 比如:商品列表:/items/queryItems.action @RequestMapping("/items") public class ItemsController { @Autowired private ItemsService itemsService; // 商品分类 //itemtypes表示最终将方法返回值放在request中的key @ModelAttribute("itemtypes") public Map<String, String> getItemTypes() { Map<String, String> itemTypes = new HashMap<String, String>(); itemTypes.put("101", "数码"); itemTypes.put("102", "母婴"); return itemTypes; } // 商品查询 @RequestMapping("/queryItems") public ModelAndView queryItems(HttpServletRequest request, ItemsQueryVo itemsQueryVo) throws Exception { // 测试forward后request是否可以共享 System.out.println(request.getParameter("id")); // 调用service查找 数据库,查询商品列表 List<ItemsCustom> itemsList = itemsService.findItemsList(itemsQueryVo); // 返回ModelAndView ModelAndView modelAndView = new ModelAndView(); // 相当 于request的setAttribut,在jsp页面中通过itemsList取数据 modelAndView.addObject("itemsList", itemsList); // 指定视图 // 下边的路径,如果在视图解析器中配置jsp路径的前缀和jsp路径的后缀,修改为 // modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp"); // 上边的路径配置可以不在程序中指定jsp路径的前缀和jsp路径的后缀 modelAndView.setViewName("items/itemsList"); return modelAndView; } // 商品信息修改页面显示 // @RequestMapping("/editItems") // 限制http请求方法,可以post和get // @RequestMapping(value="/editItems",method={RequestMethod.POST,RequestMethod.GET}) // public ModelAndView editItems()throws Exception { // // //调用service根据商品id查询商品信息 // ItemsCustom itemsCustom = itemsService.findItemsById(1); // // // 返回ModelAndView // ModelAndView modelAndView = new ModelAndView(); // // //将商品信息放到model // modelAndView.addObject("itemsCustom", itemsCustom); // // //商品修改页面 // modelAndView.setViewName("items/editItems"); // // return modelAndView; // } @RequestMapping(value = "/editItems", method = { RequestMethod.POST, RequestMethod.GET }) // @RequestParam里边指定request传入参数名称和形参进行绑定。 // 通过required属性指定参数是否必须要传入 // 通过defaultValue可以设置默认值,如果id参数没有传入,将默认值和形参绑定。 public String editItems(Model model, @RequestParam(value = "id", required = true) Integer items_id) throws Exception { // 调用service根据商品id查询商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(items_id); //判断商品是否为空,根据id没有查询到商品,抛出异常,提示用户商品信息不存 在 // if(itemsCustom == null){ // throw new CustomException("修改的商品信息不存在!"); // } // 通过形参中的model将model数据传到页面 // 相当于modelAndView.addObject方法 model.addAttribute("items", itemsCustom); return "items/editItems"; } //查询商品信息,输出json ///itemsView/{id}里边的{id}表示占位符,通过@PathVariable获取占位符中的参数, //如果占位符中的名称和形参名一致,在@PathVariable可以不指定名称 @RequestMapping("/itemsView/{id}") public @ResponseBody ItemsCustom itemsView(@PathVariable("id") Integer id)throws Exception{ //调用service查询商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(id); return itemsCustom; } // 商品信息修改提交 // 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult // bindingResult接收校验出错信息 // 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。 // value={ValidGroup1.class}指定使用ValidGroup1分组的 校验 // @ModelAttribute可以指定pojo回显到页面在request中的key @RequestMapping("/editItemsSubmit") public String editItemsSubmit( Model model, HttpServletRequest request, Integer id, @ModelAttribute("items") @Validated(value = { ValidGroup1.class }) ItemsCustom itemsCustom, BindingResult bindingResult, MultipartFile items_pic//接收商品图片 ) throws Exception { // 获取校验错误信息 if (bindingResult.hasErrors()) { // 输出错误信息 List<ObjectError> allErrors = bindingResult.getAllErrors(); for (ObjectError objectError : allErrors) { // 输出错误信息 System.out.println(objectError.getDefaultMessage()); } // 将错误信息传到页面 model.addAttribute("allErrors", allErrors); //可以直接使用model将提交pojo回显到页面 model.addAttribute("items", itemsCustom); // 出错重新到商品修改页面 return "items/editItems"; } //原始名称 String originalFilename = items_pic.getOriginalFilename(); //上传图片 if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){ //存储图片的物理路径 String pic_path = "F:\\develop\\upload\\temp\\"; //新的图片名称 String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf(".")); //新图片 File newFile = new File(pic_path+newFileName); //将内存中的数据写入磁盘 items_pic.transferTo(newFile); //将新图片名称写到itemsCustom中 itemsCustom.setPic(newFileName); } // 调用service更新商品信息,页面需要将商品信息传到此方法 itemsService.updateItems(id, itemsCustom); // 重定向到商品查询列表 // return "redirect:queryItems.action"; // 页面转发 // return "forward:queryItems.action"; return "success"; } // 批量删除 商品信息 @RequestMapping("/deleteItems") public String deleteItems(Integer[] items_id) throws Exception { // 调用service批量删除商品 // ... return "success"; } // 批量修改商品页面,将商品信息查询出来,在页面中可以编辑商品信息 @RequestMapping("/editItemsQuery") public ModelAndView editItemsQuery(HttpServletRequest request, ItemsQueryVo itemsQueryVo) throws Exception { // 调用service查找 数据库,查询商品列表 List<ItemsCustom> itemsList = itemsService.findItemsList(itemsQueryVo); // 返回ModelAndView ModelAndView modelAndView = new ModelAndView(); // 相当 于request的setAttribut,在jsp页面中通过itemsList取数据 modelAndView.addObject("itemsList", itemsList); modelAndView.setViewName("items/editItemsQuery"); return modelAndView; } // 批量修改商品提交 // 通过ItemsQueryVo接收批量提交的商品信息,将商品信息存储到itemsQueryVo中itemsList属性中。 @RequestMapping("/editItemsAllSubmit") public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo) throws Exception { return "success"; } }
页面定义:
ps:下面省略了表单的提交的标签内容

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>查询商品列表</title> <script type="text/javascript"> function deleteItems(){ //提交form document.itemsForm.action="${pageContext.request.contextPath }/items/deleteItems.action"; document.itemsForm.submit(); } function queryItems(){ //提交form document.itemsForm.action="${pageContext.request.contextPath }/items/queryItems.action"; document.itemsForm.submit(); } </script> </head> <body> 当前用户:${username }, <c:if test="${username!=null }"> <a href="${pageContext.request.contextPath }/logout.action">退出</a> </c:if> <form name="itemsForm" action="${pageContext.request.contextPath }/items/queryItems.action" method="post"> 查询条件: <table width="100%" border=1> <tr> <td> 商品名称:<input name="itemsCustom.name" /> 商品类型: <select name="itemtype"> <c:forEach items="${itemtypes }" var="itemtype"> <option value="${itemtype.key }">${itemtype.value }</option> </c:forEach> </select> </td> <td><input type="button" value="查询" onclick="queryItems()"/> <input type="button" value="批量删除" onclick="deleteItems()"/> </td> </tr> </table> 商品列表: <table width="100%" border=1> <tr> <td>选择</td> <td>商品名称</td> <td>商品价格</td> <td>生产日期</td> <td>商品描述</td> <td>操作</td> </tr> <c:forEach items="${itemsList }" var="item"> <tr> <td><input type="checkbox" name="items_id" value="${item.id}"/></td> <td>${item.name }</td> <td>${item.price }</td> <td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td> <td>${item.detail }</td> <td><a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td> </tr> </c:forEach> </table> </form> </body> </html>
2.2 list绑定
2.2.1 需求
通常在需要批量提交数据时,将提交的数据绑定到list<pojo>中,比如:成绩录入(录入多门课成绩,批量提交),
本例子需求:批量商品修改,在页面输入多个商品信息,将多个商品信息提交到controller方法中。
2.2.2 表现层实现
controller方法定义:
1、进入批量商品修改页面(页面样式参考商品列表实现)
2、批量修改商品提交 使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list<pojo>属性

页面定义:

(ps: 以上作为多条文本框,表单提交)
1.1 map绑定
也通过在包装pojo中定义map类型属性。
在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。
包装类中定义Map对象如下:
Public class QueryVo { private Map<String, Object> itemInfo = new HashMap<String, Object>(); //get/set方法.. }
页面定义如下:
<tr> <td>学生信息:</td> <td> 姓名:<inputtype="text"name="itemInfo['name']"/> 年龄:<inputtype="text"name="itemInfo['price']"/> .. .. .. </td> </tr>
Contrller方法定义如下:
public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
System.out.println(queryVo.getStudentinfo());
}
3 springmvc校验
3.1 校验理解
项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。
服务端校验:
控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
持久层dao:一般是不校验的。
3.2 springmvc校验需求
springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 可以扫描controller、service、... 这里让扫描controller,指定controller的包 --> <context:component-scan base-package="cn.itcast.ssm.controller"></context:component-scan> <!-- 静态资源解析 包括 :js、css、img、.. --> <mvc:resources location="/js/" mapping="/js/**"/> <mvc:resources location="/img/" mapping="/img/**"/> <!--注解映射器 --> <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> --> <!--注解适配器 --> <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> --> <!-- 使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置 mvc:annotation-driven默认加载很多的参数绑定方法, 比如json转换解析器就默认加载了,如果使用mvc:annotation-driven不用配置上边的RequestMappingHandlerMapping和RequestMappingHandlerAdapter 实际开发时使用mvc:annotation-driven --> <mvc:annotation-driven conversion-service="conversionService" validator="validator"></mvc:annotation-driven> <!-- 视图解析器 解析jsp解析,默认使用jstl标签,classpath下的得有jstl的包 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置jsp路径的前缀 --> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 配置jsp路径的后缀 --> <property name="suffix" value=".jsp"/> </bean> <!-- 自定义参数绑定 --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <!-- 转换器 --> <property name="converters"> <list> <!-- 日期类型转换 --> <bean class="cn.itcast.ssm.controller.converter.CustomDateConverter"/> </list> </property> </bean> <!-- 校验器 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <!-- hibernate校验器--> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties --> <property name="validationMessageSource" ref="messageSource" /> </bean> <!-- 校验错误信息配置文件 --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <!-- 资源文件名--> <property name="basenames"> <list> <value>classpath:CustomValidationMessages</value> </list> </property> <!-- 资源文件编码格式 --> <property name="fileEncodings" value="utf-8" /> <!-- 对资源文件内容缓存时间,单位秒 --> <property name="cacheSeconds" value="120" /> </bean> <!-- 全局异常处理器 只要实现HandlerExceptionResolver接口就是全局异常处理器 --> <bean class="cn.itcast.ssm.exception.CustomExceptionResolver"></bean> <!-- 文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设置上传文件的最大尺寸为5MB --> <property name="maxUploadSize"> <value>5242880</value> </property> </bean> <!--拦截器 --> <mvc:interceptors> <!--多个拦截器,顺序执行 --> <!-- 登陆认证拦截器 --> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="cn.itcast.ssm.interceptor.LoginInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <!-- /**表示所有url包括子url路径 --> <mvc:mapping path="/**"/> <bean class="cn.itcast.ssm.interceptor.HandlerInterceptor1"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="cn.itcast.ssm.interceptor.HandlerInterceptor2"></bean> </mvc:interceptor> </mvc:interceptors> </beans>
校验思路:
页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。
具体需求:
商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。
3.3 环境准备
hibernate的校验框架validation所需要jar包:

3.4 配置校验器(springmvc.xml)
<!-- 校验器 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <!-- hibernate校验器--> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties --> <property name="validationMessageSource" ref="messageSource" /> </bean> <!-- 校验错误信息配置文件 --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <!-- 资源文件名--> <property name="basenames"> <list> <value>classpath:CustomValidationMessages</value> </list> </property> <!-- 资源文件编码格式 --> <property name="fileEncodings" value="utf-8" /> <!-- 对资源文件内容缓存时间,单位秒 --> <property name="cacheSeconds" value="120" /> </bean>
3.5 校验器注入到处理器适配器中
<mvc:annotation-driven conversion-service="conversionService" validator="validator"></mvc:annotation-driven>
3.6 在pojo中添加校验规则
在ItemsCustom.java中添加校验规则:

3.7 CustomValidationMessages.properties
在CustomValidationMessages.properties配置校验错误信息:

3.8 捕获校验错误信息
//在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult bindingResult接收校验出错信息 //注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。 @RequestMapping("/editItemsSubmit") public String editItemsSubmit( HttpServletRequest request,Integer id,@Validated ItemsCustom itemsCustom,BindingResult bindingResult) throws Exception {
3.9 在页面显示校验错误信息
在controller中将错误信息传到页面即可。

页面显示错误信息:

3.10 分组校验
ps:任意将几条校验规则可以分为不同组(不同组规则可以重复),组名为自定义定义不同接口的名字
3.10.1 需求
在pojo中定义校验规则,而pojo是被多个 controller所共用,当不同的controller方法对同一个pojo进行校验,但是每个controller方法需要不同的校验。
解决方法:
定义多个校验分组(其实是一个java接口),分组中定义有哪些规则
每个controller方法使用不同的校验分组
3.10.2 校验分组

3.10.3 在校验规则中添加分组

ps:验证规则上可以同时定义多个组
3.10.4 在controller方法使用指定分组的校验

4 数据回显
4.1 什么数据回显
提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面。
ps:即表单的数据,提交后还在页面上(一般用于提交失败,方便重新修改)
4.2 pojo数据回显方法
1、springmvc默认对pojo数据进行回显。
1,pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)
(ps:也就是说,跳转的页面自动从传入的数据中取值,但是页面的属性名称和传入的对象名称必须对应,也可以通过注解自定义名称)
![]()
2,使用@ModelAttribute指定pojo回显到页面在request中的key(方法中添加注解,自定义前端属性的名称)

前端页面:

2、@ModelAttribute还可以将方法的返回值传到页面
在商品查询列表页面,通过商品类型查询商品信息。
在controller中定义商品类型查询方法,最终将商品类型传到页面。

页面上可以得到itemTypes数据。

3、使用最简单方法,直接使用model存入,可以不用@ModelAttribute

4.3 简单类型数据回显
使用最简单方法使用model。
model.addAttribute("id", id);
ps:默认对象参数会被自动存入request域中,简单类型需要手动存入,model对象的数据会自动存入request域中。


浙公网安备 33010602011771号