1. 使用Spring的Validation接口进行验证
Spring提供了一个Validator接口,可以使用它来验证对象。Validator接口通过使用Errors对象工作,以便在验证时,验证器可以向Errors对象报告验证失败。
org.springframework.validation.Validator接口代码如下:
package org.springframework.validation;
public interface Validator {
boolean supports(Class<?> var1);
void validate(Object var1, Errors var2);
}其中,
- supports方法:当前Validator需要验证的Class是谁,
- validate方法:验证给定的对象(的属性),并且在验证错误的情况下,将这些对象注册到给定的errors对象。
比如一个普通的Person对象类为:
public class Person {
private String name;
private int age;
// the usual getters and setters...
}可以为其搞一个实现Validator接口的验证器。ValidationUtils帮助类也是Spring框架提供的。下面的示例为Person实例实现了Validator的代码:
public class PersonValidator implements Validator {
/**
* 验证的类为: Person.class
*/
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}如果涉及对象里嵌套对象,也想支持嵌套类的验证,那么也是可行的,示例如下:
public class CustomerValidator implements Validator {
private final Validator addressValidator;
public CustomerValidator(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;
}
/**
* 前提:Customer.class 嵌套其他子类,比如Address.class
* 当前验证类CustomerValidator 不但会验证Customer类,也会校验Customer类的子类Address
*/
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();
}
}
}2. 操作Bean的类_BeanWrapper
首先org.springframework.beans 包的都是遵循javabean标准的。javabean可简单理解为:由一个默认的无参构造函数的类。
而在bean包中一个非常重要的类是BeanWrapper接口及其相应的实现(BeanWrapperImpl)。
- BeanWrapper提供了设置和获取属性值(单独或批量)、获取属性描述符以及查询属性以确定它们是可读还是可写的功能。
- 此外,BeanWrapper提供了对嵌套属性的支持,允许将子属性上的属性设置为无限深度。
- BeanWrapper还支持添加标准JavaBeans PropertyChangeListeners和VetoableChangeListeners的能力,而不需要在目标类中支持代码。
- 最后但并非最不重要的是,BeanWrapper提供了对设置索引属性的支持。应用程序代码通常不直接使用BeanWrapper,而是由DataBinder和BeanFactory使用。
设置和获取属性是通过BeanWrapper的setPropertyValue和getPropertyValue重载方法变体完成的。比如存在以下两个类Company 和Employee ,Employee 是Company 的子类:
public class Company {
private String name;
private Employee managingDirector;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Employee getManagingDirector() {
return this.managingDirector;
}
public void setManagingDirector(Employee managingDirector) {
this.managingDirector = managingDirector;
}
}public class Employee {
private String name;
private float salary;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public float getSalary() {
return salary;
}
public void setSalary(float salary) {
this.salary = salary;
}
}下面的代码片段展示了如何检索和操作实例化的Companys和Employees的一些属性的一些示例:
BeanWrapper company = new BeanWrapperImpl(new Company());
// 设置 company name..
company.setPropertyValue("name", "Some Company Inc.");
// ...也可以这样做:
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);
// 现在,创建一个公司的名为Jim Stravinsky的公司董事:
BeanWrapper jim = new BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());
// 通过公司拿到董事的工资
Float salary = (Float) company.getPropertyValue("managingDirector.salary");3. Spring 类型转换
在Spring中,提供了一个类型转换的接口,名为:Converter(org.springframework.core.convert.converter):
package org.springframework.core.convert.converter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@FunctionalInterface
public interface Converter<S, T> {
@Nullable
T convert(S var1);
default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
Assert.notNull(after, "After Converter must not be null");
return (s) -> {
T initialResult = this.convert(s);
return initialResult != null ? after.convert(initialResult) : null;
};
}
}上述接口中的convert方法,是将类型S转换为T的一个过程。
而本身Spring也提供了一些实现Converter接口的实现类,这些实现类位于:org.springframework.core.convert.support包。比如StringToBooleanConverter :
package org.springframework.core.convert.support;
import java.util.HashSet;
import java.util.Set;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
final class StringToBooleanConverter implements Converter<String, Boolean> {
private static final Set<String> trueValues = new HashSet<>(8);
private static final Set<String> falseValues = new HashSet<>(8);
static {
trueValues.add("true");
trueValues.add("on");
trueValues.add("yes");
trueValues.add("1");
falseValues.add("false");
falseValues.add("off");
falseValues.add("no");
falseValues.add("0");
}
@Override
@Nullable
public Boolean convert(String source) {
String value = source.trim();
if (value.isEmpty()) {
return null;
}
value = value.toLowerCase();
if (trueValues.contains(value)) {
return Boolean.TRUE;
}
else if (falseValues.contains(value)) {
return Boolean.FALSE;
}
else {
throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
}
}
}
- 如果想集中整个类层次结构的转换逻辑,可以考虑使用ConverterFactory。
- 如果想设计一个复杂但类型签名更弱的转换器,可以考虑使用GenericConverter,一个很好的例子是:ArrayToCollectionConverter。
- 如果只有在特定条件为真时才运行Converter。例如,只在目标字段上有特定的注释时才运行Converter,或者可能希望只在目标类上定义了特定的方法(例如静态valueOf方法)时才运行Converter。ConditionalGenericConverter是GenericConverter和ConditionalConverter接口的结合,可以让你定义这样的自定义匹配标准:
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}实现ConditionalGenericConverter的一个很好的例子为:IdToEntityConverter
- 在convert包中,有一个类叫:ConversionService 。它定义了一个用于在运行时执行类型转换逻辑的统一API。
package org.springframework.core.convert;
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType);
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}- 大多数converonservice实现类还是实现了ConverterRegistry,它提供了一个用于注册转换器的SPI。在内部,converonservice实现委托给其注册的转换器来执行类型转换逻辑。
- 在core.convert.support包中提供了一个健壮的ConversionService实现:GenericConverversionService,它是适合在大多数环境中使用的通用实现。
- 而ConversionServiceFactory 为创建通用的ConversionService 配置提供了方便的工厂。
4. Spring字段格式
针对客户端环境的类型转换,Spring提供Formatter SPI–为客户端环境提供了一个简单而健壮的PropertyEditor实现替代方案。
但是在实现通用类型转换逻辑时,还是使用Converter SPI的。比如,在java.util.Date和Long之间进行转换。
而当处于客户端环境(如web应用程序)中工作并需要解析和打印本地化的字段值时,才需要考虑使用Formatter SPI。
package org.springframework.format;
public interface Formatter<T> extends Printer<T>, Parser<T> {
}Formatter继承自Printer和Parser接口。下面显示了这两个接口的定义:
public interface Printer<T> {
String print(T fieldValue, Locale locale);
}import java.text.ParseException;
public interface Parser<T> {
T parse(String clientValue, Locale locale) throws ParseException;
}如果创建自己的Formatter,直接实现Formatter接口即可。为方便使用,spring的format 包已经提供了一些Formatter实现。比如DateFormatter 和NumberStyleFormatter等。
package org.springframework.format.datetime;
public final class DateFormatter implements Formatter<Date> {
private String pattern;
public DateFormatter(String pattern) {
this.pattern = pattern;
}
public String print(Date date, Locale locale) {
if (date == null) {
return "";
}
return getDateFormat(locale).format(date);
}
public Date parse(String formatted, Locale locale) throws ParseException {
if (formatted.length() == 0) {
return null;
}
return getDateFormat(locale).parse(formatted);
}
protected DateFormat getDateFormat(Locale locale) {
DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
dateFormat.setLenient(false);
return dateFormat;
}
}
















