标签:spring
关于是否把数据校验看做业务逻辑,有支持也有反对。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的简单替代来使用,当然也会在本章中讨论。
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。
前面说了数据绑定和校验。那么最后,我们需要对校验错误输出相应的错误信息。在上面我们给出的例子中,‘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。
翻译的不准确的地方还望大家指出来~
标签:spring
原文地址:http://blog.csdn.net/u012345283/article/details/41324003