JSR 303 – Bean Validation
JSR 303是JAVA EE 6中的一项子规范,Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API。缺省的元数据是 Java Annotations,通过使用 XML 可以对Java注解信息进行覆盖和扩展;
例如: @NotNull, @Max, @ZipCode, 就可以确保数据模型(JavaBean)的正确性;
Constraint
constraint在JSR的规范上是这样说的:A constraint on a JavaBean is expressed through one or more annotations. An annotation is considered a constraint definition if its retention policy contains RUNTIMEand if the annotation itself is annotated withjavax.validation.Constraint
大概含义是:使用Constraint来使一个或更多个annotaion起作用
其定义如下:
@Documented
@Target({ ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface Constraint {
/**
* {@link ConstraintValidator} classes must reference distinct target types
* for a given {@link ValidationTarget}
* If two {@code ConstraintValidator}s refer to the same type,
* an exception will occur.
* <p/>
* At most one {@code ConstraintValidator} targeting the array of parameters of
* methods or constructors (aka cross-parameter) is accepted. If two or more
* are present, an exception will occur.
*
* @return array of (@code ConstraintValidator} classes implementing the constraint
*/
Class<? extends ConstraintValidator<?, ?>>[] validatedBy();
}
使用方法:
通过validatedBy指定校验器的class,Validation API的实现会自动加载你所指定的校验器,如下:
@Constraint(validatedBy = OrderNumberValidator.class)
package com.acme.constraint;
/**
* Mark a String as representing a well formed order number
*/
@Documented
*@Constraint(validatedBy = OrderNumberValidator.class)*
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface OrderNumber {
String message() default "{com.acme.constraint.OrderNumber.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
估计有人会有疑问,为什么自带的API都没有指定Validator,这个是因为如果是有官方定义的Api,在相应的Api实现上已经把指定的Validator加进去,例如:
Hibernate-validator是Validator Api实现,在ConstraintHelper的有如下代码:
public ConstraintHelper() {
Map<Class<? extends Annotation>, List<? extends Class<?>>> tmpConstraints = newHashMap();
putConstraint( tmpConstraints, AssertFalse.class, AssertFalseValidator.class );
putConstraint( tmpConstraints, AssertTrue.class, AssertTrueValidator.class );
putConstraint( tmpConstraints, CNPJ.class, CNPJValidator.class );
....省略部分代码
putConstraint( tmpConstraints, URL.class, URLValidator.class );
this.builtinConstraints = Collections.unmodifiableMap( tmpConstraints );
}
目前已有的Validator
Validator的官方实现Hibernate新增的Validator
如何使用:
写代码引入校验
Validator validator = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory()
.getValidator();
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( anInstance );
在Spring中可以配置如下:
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass"
value="org.hibernate.validator.HibernateValidator" />
</bean>
Validatior Api和Hibernate-Validator的关系
用过SPI来扩展实现,在Validator中
private static class GetValidationProviderListAction implements PrivilegedAction<List<ValidationProvider<?>>> {
private static final WeakHashMap<ClassLoader, SoftReference<List<ValidationProvider<?>>>> providersPerClassloader =
new WeakHashMap<ClassLoader, SoftReference<List<ValidationProvider<?>>>>();
public static List<ValidationProvider<?>> getValidationProviderList() {
final GetValidationProviderListAction action = new GetValidationProviderListAction();
if ( System.getSecurityManager() != null ) {
return AccessController.doPrivileged( action );
}
else {
return action.run();
}
}
public List<ValidationProvider<?>> run() {
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
List<ValidationProvider<?>> validationProviderList = loadProviders( classloader );
return validationProviderList;
}
private List<ValidationProvider<?>> loadProviders(ClassLoader classloader) {
ServiceLoader<ValidationProvider> loader = ServiceLoader.load( ValidationProvider.class, classloader );
Iterator<ValidationProvider> providerIterator = loader.iterator();
List<ValidationProvider<?>> validationProviderList = new ArrayList<ValidationProvider<?>>();
while ( providerIterator.hasNext() ) {
try {
validationProviderList.add( providerIterator.next() );
}
catch ( ServiceConfigurationError e ) {
}
}
return validationProviderList;
}
}
下一章将对如何使用Annotation和Xml两种方式来实现对Java Bean的校验