SpringMVC - 数据绑定

标签: SpringMVC

1. 概述

在执行程序时,Spring MVC会根据客户端请求参数的不同,将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中。这种将请求消息数据与后台方法参数建立连接的过程就是Spring MVC中的数据绑定

在早期springMVC使用PropertyEditor,后期使用convertor进行任意类型的转换,springMVC提供了很多convertor(转换器),在特使情况下(日期数据的绑定)需要自定义convertor

数据绑定的流程
SpringMVC 有支持的默认参数类型,我们直接在形参上给出这些默认类型的声明,就能直接使用了

//HttpServletRequest 对象

//HttpServletResponse 对象

//HttpSession 对象

//Model/ModelMap 对象 

@RequestMapping("/defaultParameter")
public ModelAndView defaultParameter(HttpServletRequest request,HttpServletResponse response,HttpSession session,Model model,ModelMap modelMap) throws Exception{
    request.setAttribute("requestParameter", "request类型");
    response.getWriter().write("response");
    session.setAttribute("sessionParameter", "session类型");
    //ModelMap是Model接口的一个实现类,作用是将Model数据填充到request域
    //即使使用Model接口,其内部绑定还是由ModelMap来实现
    model.addAttribute("modelParameter", "model类型");
    modelMap.addAttribute("modelMapParameter", "modelMap类型");
     
    ModelAndView mv = new ModelAndView();
    mv.setViewName("view/success.jsp");
    return mv;
}

而对于其他类型的参数,SpringMVC通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中,数据绑定的核心部件是DataBinder
在这里插入图片描述

① SpringMVC主框架将ServletRequest对象以及目标法方法的入参传递给WebDataBinderFactory实例,以创建DataBinder(数据绑定器)实例对象

DataBinder调用装配在SpringMVC上下文的ConversionService组件进行数据类型转换,数据格式化工作,将Servlet中的请求信息填充到入参对象中

③ 调用Validator组件对已经绑定了请求参数的入参对象进行数据合法性校验,并最终生成绑定结果BindingData对象,如果在校验过程中出现错误,结果会放到BindingResult对象中

④ 校验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数

2. 数据绑定

① 基本类型参数

包括基本类型和 String 类型
使用要求:参数名称必须和控制器中方法的形参名称保持一致。 (严格区分大小写)

index.jsp

<form action="basicData" method="post">
    <input name="username" value="10" type="text"/>
    <input type="submit" value="提交">
</form>

Controller 代码:

@RequestMapping("/basicData")
public void basicData(int username){
    System.out.println(username);//10
}

我们这里的参数是基本数据类型,如果从前台页面传递的值为 null 或者 “”的话,那么会出现数据转换的异常,就是必须保证表单传递过来的数据不能为null或”",所以,在开发过程中,对可能为空的数据,最好将参数数据类型定义成包装类型

index.jsp

<form action="${pageContext.request.contextPath}/user/testParam" method="post">
	用户名: <input type="text" name="username"> <br>
	年龄: <input type="text" name="age"> <br>
	性别: <input type="text" name="sex"> <br>
    <input type="submit" value="提交">
</form>

UserController.java

@Controller
@RequestMapping("/user")
public class UserController2 {

    @RequestMapping(value = "/testParam",method = RequestMethod.POST)
    public String testParam(String username,Integer age,Character sex){
        System.out.println(username + "--" + age + "--" + sex);
        return "show";
    }
}

表单传递过来的数据可以为null或”",以上面代码为例,如果表单中num为”"或者表单中无num这个input,那么,Controller方法参数中的num值则为null

② POJO类型参数

  • 要求集合类型的请求参数必须在 POJO 中。在表单中请求参数名称要和 POJO 中集合属性名称相同。

index.jsp

<form action="${pageContext.request.contextPath}/user/saveUser" method="post">
	用户名: <input type="text" name="username"> <br>
    年龄: <input type="text" name="age"> <br>
    性别: <input type="text" name="sex"> <br>
    一个角色对象:<!--role为 POJO类型参数-->
    id:<input type="text" name="role.id">
    roleName:<input type="text" name="role.roleName"><br>
    <input type="submit" value="提交">
</form>

Role.java

public class Role {
    private Integer id;
    private String roleName;
    //此处省略 getter and setter toString
}

User.java

public class User {
    private Integer id;
    private String username;
    private Character sex;
    private Integer age;

    private  Role role; //POJO 类型参数
    //此处省略 getter and setter toString
 }

UserController.java

@RequestMapping(value = "/saveUser",method = RequestMethod.POST)
public String saveUser(User user){
	System.out.println(user);
    return "show";
}

③ 集杂数据类型

数组

表单中name属性相同,value不同;形参是数组,且名称相同

集合
  • 要求集合类型的请求参数必须在 POJO 中。在表单中请求参数名称要和 POJO 中集合属性名称相同

  • 给 List 集合中的元素赋值,使用下标

  • 给 Map 集合中的元素赋值,使用键值对

index.jsp

   <form action="${pageContext.request.contextPath}/user/saveUser" method="post">
       用户名: <input type="text" name="username"> <br>
       年龄: <input type="text" name="ag"> <br>
        性别: <input type="text" name="sex"> <br>
        list集合参数: <input type="text" name="list[0]">
                       <input type="text" name="list[1]"><br>
        一个角色对象:<input type="text" name="role.id">
                    <input type="text" name="role.roleName"><br>
        多个角色对象:<input type="text" name="roleList[0].id">
                    <input type="text" name="roleList[0].roleName"><br>
                    <input type="text" name="roleList[1].id">
                    <input type="text" name="roleList[1].roleName"><br>
        map集合:<input type="text" name="map[one]"><br>
                <input type="text" name="map[two]"><br>
                <input type="text" name="map[three]"><br>

        <input type="submit" value="提交">
    </form>

Role.java

public class Role {

    private Integer id;
    private String roleName;
    //此处省略 getter and setter toString
}

User.java

public class User {
    private Integer id;
    private String username;
    private Character sex;
    private Integer age;
    
    private List<String> list;
    private  Role role;
    private List<Role> roleList;
    private Map<String ,Object> map;
    //此处省略 getter and setter toString
 }

UserController.java

    @RequestMapping(value = "/saveUser",method = RequestMethod.POST)
    public String saveUser(User user){
        System.out.println(user);
        return "show";
    }

后台结果

User {
	id = null,
	username = 'zhangsan',
	sex =,
	age = 30,
	list = [hello, world],
	role = Role {
		id = 21,
		roleName = 'cook'
	},
	roleList = [Role {
			id = 23,
			roleName = 'teacher'
		}, Role {
			id = 25,
			roleName = 'student'
		}
	],
	map = {
		one = little,
		three = huge,
		two = big
	}
}

3. 自定义类型转换器

  • 表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明Spring框架内部会默认进行数据类型转换。

  • 如果想自定义数据类型转换,可以实现Converter的接口

  • 注册自定义类型转换器,在springmvc.xml配置文件中编写配置

  • 还是可以使用Formatter进行类型转换

当我们在后端对日期类型的参数进行准换,默认只转换2000/1/1类型的日期,如果输入的是2000-1-1转换就会出错

ConversionService是Spring类型转换体系的核心接口

可以利用ConversionServiceFactoryBean在Spring的IOC容器中定义一个ConversionService,Spring将自动识别出IOC容器中的ConversionService,并在Bean属性配置及Spring MVC处理方法入参绑定等场合使用它进行数据的转换

可以通过ConversionServiceFactoryBeanconverters属性注册自定义的类型转换器

<form action="${pageContext.request.contextPath}/user/testDate">
    <input type="date" name="birthday">
    <input type="submit" value="提交">
</form>

@RequestMapping("/testDate")
public String testDate(Data birthday){
    System.out.println(birthday);
    return "show";
}

自定义String–>Date类型转换器

public class StringToDateConverter implements Converter<String ,Date>{
    //把 String 类型转成日期类型
    
    @Override
    public Date convert(String source) {
        //日期类型的转换对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            date = sdf.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

在 spring 配置文件中配置类型转换器

<!-- 配置类型转换器工厂 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!-- 给工厂注入一个新的类型转换器 -->
    <property name="converters">
        <set>
       		 <!-- 配置自定义类型转换器 -->
            <bean class="com.testfan.converter.StringToDateConverter"></bean>
        </set>
    </property>
</bean>

在 annotation-driven 标签中引用配置的类型转换服务

<!-- 引用自定义类型转换器 -->
<!--注解驱动: 关联类型转换工厂-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

4. 数据格式化

数据格式化
Converter可以将一种类型转换成另一种类型,是任意Object之间的类型转换

Formatter则只能进行String与任意Object对象的转换,它提供 解析格式化 两种功能
其中:解析是将String类型字符串转换为任意Object对象,格式化是将任意Object对象转换为字符串进行格式化显示

① 实现Formatter<T> 接口

DateFormatter.java

//实现Formatter<T> 接口
public class DateFormatter implements Formatter<Date>{
    // 日期类型模板:如yyyy-MM-dd
    private String datePattern;
    // 日期格式化对象
    private SimpleDateFormat dateFormat;
 
    // 构造器,通过依赖注入的日期类型创建日期格式化对象
    public DateFormatter(String datePattern) {
        this.datePattern = datePattern;
        this.dateFormat = new SimpleDateFormat(datePattern);
    }
    // 显示Formatter<T>的T类型对象
    @Override
    public String print(Date date, Locale locale) {
        return dateFormat.format(date);
    }
    // 解析文本字符串返回一个Formatter<T>的T类型对象。
    @Override
    public Date parse(String source, Locale locale) throws ParseException {
        try {
            return dateFormat.parse(source);
        } catch (Exception e) {
            throw new IllegalArgumentException();
        }
    }
 
}

装配自定义格式化转换器

<!-- 装配自定义格式化转换器-->
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
            <list>
                <bean class="com.formatter.DataFormatter" c:_0="yyyy-MM-dd"></bean>
            </list>
    </property>
</bean>

② 使用注解

// 域对象,实现序列化接口
public class User implements Serializable{
 
    // 日期类型
    @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date birthday;
    // 正常数字类型
    @NumberFormat(style=Style.NUMBER, pattern="#,###")
    private int total;
    // 百分数类型
    @NumberFormat(style=Style.PERCENT)
    private double discount;
    // 货币类型
    @NumberFormat(style=Style.CURRENCY)
    private double money;
    ...
}

5. 数据校验

数据校验
JSR 303 是Java为Bean数据合法性校验提供的标准框架,其已经包含在JavaEE 6.0中;JSR 303 通过在Bean属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证

Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,还支持一些扩展注解

① 关于所需要的jar包
Spring4.0拥有独立的数据校验框架,同时支持 JSR 303 标准的校验框架;但其本身并没有提供 JSR 303 的实现,故必须将 JSR 303 的实现者的jar包放到类路径下。

② 关于LocalValidatorFactoryBean工厂类
该工厂类既实现了Spring的Validator接口,也实现了 JSR 303 的Validator接口;故需要在 Spring 容器中定义LocalValidatorFactoryBean,即可将其注入到需要数据校验的Bean中。

③ 关于@Valid注解
<mvc:annotation-driven/>会默认装配LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解即可让SpringMVC在进行数据绑定时,同时调用校验框架完成数据校验工作。

④ 关于校验结果
前一个表单/命令对象的校验结果保存到随后处理方法的入参中,该入参必须是BindingResultErrors类型;

需注意,需校验的Bean对象和其绑定结果对象或错误对象是成对出现的,其之间不允许声明其他的入参

在需要校验的JavaBean属性上添加相应的校验注解

public class Employee { 
    @NotNull
    private String name;
    @Email
    private String email;
    @Past
    @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date birth; 
    // ……
}

在处理器目标方法的Bean类型的入参前添加@Valid注解,并添加保存校验结果的对象(BindingResult )

@RequestMapping(value="/emp", method=RequestMethod.POST)
public String save(@Valid Employee employee, BindingResult result, Map<String, Object> map) {
    System.out.println(employee);
 	//BindingResult
    if(result.getErrorCount() > 0) {
        for(FieldError error : result.getFieldErrors()) {
            System.out.println(error.getField() + " : " + error.getDefaultMessage());
        }
 
        // 指定校验错误时所转向的定制页面
        map.put("departments", departmentDao.getDepartments());
        return "emp-edit";
    }
 
    employeeDao.save(employee);
    return "redirect:/emps";
}
版权声明:本文为weixin_43907800原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43907800/article/details/105674061

智能推荐

循环

与任何程序设计语言一样Java利用条件语句与循环结构确定流程控制,一下总结一下Java中的循环语句: while do while for switch 对于golang来说: switch非常灵活。从第一个expr为true的case开始执行,如果case带有fallthrough,程序会继续执行下一条case,不会再判断下一条case的expr,如果之后的case都有fallthrough,d...

1638 统计只差一个字符的子串数目(动态规划)

1. 问题描述: 给你两个字符串 s 和 t ,请你找出 s 中的非空子串的数目,这些子串满足替换一个不同字符以后,是 t 串的子串。换言之,请你找到 s 和 t 串中恰好只有一个字符不同的子字符串对的数目。比方说, "computer" 和 "computation"...

websocket基本原理

HTTP中一个request只能有一个response。而且这个response也是被动的,不能主动发起 因此过去的服务端推送信息是通过客户端不停的轮询实现的 websocket是双向通信协议,提供了服务端主动推送信息的能力 需要客户端(浏览器)和服务端同时支持 如果经过代理的话,还需要代理支持,否则有些代理在长时间无通信时会自动切断连接 因此WS为了保证连接不被断掉,会发心跳 WebSocket...

mybatis+ehcache二级缓存

导入jar包 mapper.xml文件开启二级缓存 pojo类实现序列化接口 配置ehcache.xml 测试...

python+opencv实现图像拼接

任务 拍摄两张图片去除相同部分,拼接在一起 原图 结果 步骤 读取两张图片 使用sift检测关键点及描述因子 匹配关键点 处理并保存关键点 得到变换矩阵 图像变换并拼接 代码实现 扩展 这里对右边图像进行变换,右边变得模糊,可以修改代码对左边图像变换 这里只有两张图片拼接,可以封装实现多张图片拼接 可以修改代码实现上下图片的拼接...

猜你喜欢

python_sklearn机器学习算法系列之AdaBoost------人脸识别(PCA,决策树)

          注:在读本文之前建议读一下之前的一片文章python_sklearn机器学习算法系列之PCA(主成分分析)------人脸识别(k-NearestNeighbor,KNN)         本文主要目的是通过一个简单的小...

memmove函数与memcpy函数的模拟实现

memmove函数和memcpy函数都是在内存复制任意类型的,但是它俩也有区别。当源区域和目标区域有重复的,memmove函数会复制缓冲区重叠的部分,而memcpy相反,会报出未知错误。 下面给出两个函数的实现 首先,memmove函数。 实现的基本原理如下图。 具体代码如下: memcpy函数的实现很简单,就直接给出源代码了...

SpringFramework核心 - IOC容器的实现 - 总结

1. 概述 把Spring技术内幕第一章和第二章过了一遍,也做了一些笔记, 对IOC容器的实现有了一定皮毛理解,现在跟着源码再过一遍总结一下IOC容器的初始化,Bean的初始化的过程,做一下总结 ① IOC容器和简单工厂模式 在开始之前,先想想我们平时是怎么使用IOC容器为我们管理Bean的,假设我们要把下面的User类交给IOC容器管理 我们不想关心如何创建一个User对象实例的,仅仅在需要他的...

Python和Django的安装

个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈  一、下载并安装Python Python 官方下载地址:http://www.python.org/ftp/python/ 我们这里选择的是 Python 2.7.2 。虽然目前最新版是Python 3.2.2, 但是Django目前还不支持 Python 3.2.2。 安装步骤很简单,双击安装包开...