6.校验,数据绑定和类型转换(6.1 - 6.3)

6.校验,数据绑定和类型转换


6.1 介绍

关于是否把数据校验看做业务逻辑,有支持也有反对。Spring设计了一套校验规则(包括数据绑定)并没有否定任何一种观点。特定地校验不应该依赖于web层,而是应该易于局部化而且可以嵌入到任何可用的校验器(validator)中。考虑到以上几点,Spring提出了一个校验器(Validator)接口,它是校验的基础并且非常出众,而最关键的是它在每一层中都可以使用。

数据绑定对于将用户的输入动态绑定到实体上非常有用。Spring提供数据绑定器(DataBinder)来准确地完成这项任务。校验器(Validator)和数据绑定器(DataBinder)构成了validation这个包,它是MVC框架中主要使用的包,但是并不是局限于MVC。

在Spring中,Bean包装类(BeanWrapper)是一个基本概念并且被广泛用于很多地方。也许你并不需要直接使用BeanWrapper,但因为这是说明文档,我们觉得需要按顺序来介绍。所以这一章会介绍BeanWrapper。当你在将数据绑定到对象的过程中最有可能会使用到它。

Spring的DataBinder和低级的BeanWrapper都使用属性编辑器(PropertyEditors)来解析和格式化属性值。属性编辑器(PropertyEditors)的概念在本章中也有提到,它是JavaBeans规定的一部分。Spring3引入了“core.convert”包和一个高级的“format”包。前者提供了一个通用的类型转换工具,而后者则用来格式化UI(显示)域值。这些新的包可以被当做PropertyEditors的简单替代来使用,当然也会在本章中讨论。


6.2 使用Spring的校验器接口来做校验

Spring的一个特点就是使用校验器(Validator)接口来校验对象。校验器接口使用Errors对象来配合校验工作,校验过程中的校验失败都会被绑定到Errors对象上。

考虑一个简单的数据对象:

public class Person {
	
	private String name;
	private int age;
	
	// the usual getters and setters...
}

我们尝试通过实现org.springframework.validation.Validator接口的两个方法来为Person类提供校验:

• supports(Class)- 用来校验器是否可以校验给定的类

• validate(Object, org.springframework.validation.Errors) - 用来校验给定的对象,万一发生校验错误,那么把这些错误绑定到Errors对象上

当你知道Spring框架还提供了ValidationUtils这个帮助类,你会发现实现一个Validator是多么的简单。

public class PersonValidator implements Validator {
	
	/**
	* This Validator validates just Person instances
	*/
	
	public boolean supports(Class clazz) {
		return Person.class.equals(clazz);
	}
	
	public void validate(Object obj, Errors e) {
		ValidationUtils.rejectIfEmpty(e, "name", "name.empt");
		Person p = (Person) obj;
		if(p.getAge() < 0) {
			e.rejectValue("age", "negativevalue");
		} else if(p.getAge() > 110) {
			e.rejectValue("age", "too.darn.old");
		}
	}
}


如上所示,ValidationUtils类的静态方法rejectIfEmpty(..)是用来校验'name'这个属性,确保其不为null或空字符串。查看ValidationUtils类的Javadoc,可以找到它提供的除了上面例子显示之外的其他功能。

当需要实现一个验证器类来验证一个“富对象”(也就是说成员变量中还有自定义的引用变量)时,更好的方式是为每个嵌套的类都提供一个各自的校验器实现类。举个例子,Customer是一个“富对象”,它由2个String属性(姓和名)和一个复杂的Address对象组成。Address对象也可能脱离Customer而独立使用,所以一个AddressValidator已经被实现了。如果你想要CustomerValidator重用AddressValidator的逻辑,当然你并不想依靠“复制粘贴”这种手段,那么可以在你的CustomerValidator中依赖注入或是实例化一个AddressValidator,比如:

public class CustomerValidator implements Validator {
	private final Validator addressValidator;

	public Customer Validator(Validator addressValidator) {
		if (addressValidator == null) {
			throw new IllegalArgumentException(
					"The supplied [Validator] is required and must not be null.");
		}
		if (!addressValidator.supports(Address.class)) {
			throw new IllegalArgumentException(
					"The supplied [Validator] must support the validation of [Address] instances.");
		}
		this.addressValidator = addressValidator;
	}

	/**
	 * This Validatorvalidates Customerinstances, and any subclasses of
	 * Customertoo
	 */
	public boolean supports(Class clazz) {
		return Customer.class.isAssignableFrom(clazz);
	}

	public void validate(Object target, Errors errors) {
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName",
				"field.required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname",
				"field.required");
		Customer customer = (Customer) target;
		try {
			errors.pushNestedPath("address");
			ValidationUtils.invokeValidator(this.addressValidator,
					customer.getAddress(), errors);
		} finally {
			errors.popNestedPath();
		}
	}
}

校验中产生的错误会被绑定到Errors对象上并被传递给校验器。在Spring Web MVC中你可以使用<spring:bind/>标签来检查这些错误信息,你也可以自己检查这些错误对象。更多关于这些方法的信息可以参考Javadoc。


6.3 把错误代码解析成错误消息

前面说了数据绑定和校验。那么最后,我们需要对校验错误输出相应的错误信息。在上面我们给出的例子中,'name'和'age'这两个字段没有通过校验(被reject掉了)。如果我们想要通过MessageSource来输出错误信息,那么当某个字段没有通过校验的时候(在例子中是'name'和'age'字段),需要给定一个错误代码。当我们调用(不管是直接地,间接地,或者是ValidationUtils类)rejectValue方法或是Errors接口的其他reject方法时,底层的实现不仅会注册你传入的错误代码,而且还会注册几个额外的错误代码。具体注册什么代码主要是由MessageCodesResolver决定的。默认会使用DefaultMessageCodesResolver,它不仅会注册一个你提供的错误代码,并且还会注册你传入reject方法的字段名。所以在例子中你使用rejectValue("age",  "too.darn.old")reject一个字段,那么除了too.darn.old这个代码以外,Spring还会注册too.darn.old.age和too.darn.old.age.int(所以第一会注册这个字段名,第二会注册这个字段类型);这么做的目的,是为了帮助开发者可以更方便的匹配错误信息。更多关于MessageCodesResolver的信息和默认策略可以参考MessageCodesResolver和DefaultMessageCodesResolver的Javadocs。



翻译的不准确的地方还望大家指出来~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值