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 + "'");
		}
	}
}

spring表单验证_java

  • 如果想集中整个类层次结构的转换逻辑,可以考虑使用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;
    }
}