Java bean validation 规范与参考实现

JSR 303 – Bean Validation 是一个数据验证的规范。 2009年11月正式发布最终版本。
在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。应用程序必须通过某种手段来确保输入进来的数据从语义上来讲是正确的。在通常的情况下,应用程序是分层的,比如表现层,业务层,持久化层等,很多时候同样的数据验证逻辑会出现在不同的层,这样就会导致代码冗余和一些管理的问题,比如说语义的一致性等。为了避免这样的情况发生,最好是将验证逻辑与相应的域模型进行绑定,为各个层提供统一的数据校验。

Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API。缺省的元数据是 Java Annotations,通过使用 XML 可以对Java注解信息进行覆盖和扩展。在应用程序中,通过使用 Bean Validation 或是你自己定义的 constraint,例如 @NotNull, @Max, @ZipCode, 就可以确保数据模型(JavaBean)的正确性。在JSR303规范中,constraint 可以附加到字段,getter 方法,类或者接口上面。对于一些特定的需求,用户可以很容易的开发定制化的 constraint。Bean Validation 是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回。

Hibernate Validator 4.0是 JSR 303 的参考实现。

IBM developerWorks网站有一篇很好的文章介绍它。

JSR 349是Bean Validation 1.1规范,根据社区的反馈对JSR 303进行了优化和加强。 可以点击这里查看细节。
主要围绕一下方面进行工作。

  • 集成其它最新发布的JSR
  • JAX-RS: 对HTTP method进行参数和返回值的校验
  • JAXB: 集成JAXB.
  • JPA: 提升JPA的集成
  • CDI: 允许CDI风格的注入
  • CDI: 集成方法级别的校验
  • EJB: 集成方法级别的校验
  • JSF: 集成JSF

Hibernate Validator 5.x 是Bean Validation 1.1参考实现。

hibernate validator官方文档提供了注解的列表。

AnnotationSupported data typesUseHibernate metadata impact
@AssertFalseBoolean, booleanChecks that the annotated element is falseNone
@AssertTrueBoolean, booleanChecks that the annotated element is trueNone
@DecimalMax(value=, inclusive=)BigDecimal, BigInteger, CharSequence, byte, short, int, long and the respective wrappers of the primitive types; Additionally supported by HV: any sub-type of NumberChecks whether the annotated value is less than the specified maximum, when inclusive=false. Otherwise whether the value is less than or equal to the specified maximum. The parameter value is the string representation of the max value according to the BigDecimal string representation.None
@DecimalMin(value=, inclusive=)BigDecimal, BigInteger, CharSequence, byte, short, int, long and the respective wrappers of the primitive types; Additionally supported by HV: any sub-type of NumberChecks whether the annotated value is larger than the specified minimum, when inclusive=false. Otherwise whether the value is larger than or equal to the specified minimum. The parameter value is the string representation of the min value according to the BigDecimal string representation.None
@Digits(integer=, fraction=)BigDecimal, BigInteger, CharSequence, byte, short, int, long and the respective wrappers of the primitive types; Additionally supported by HV: any sub-type of NumberChecks whether the annoted value is a number having up to integer digits and fraction fractional digitsDefines column precision and scale
@Futurejava.util.Date, supported by HV, if the Joda Time date/time API is on the class path: any implementations of ReadablePartial and ReadableInstantChecks whether the annotated date is in the futureNone
@Max(value=)BigDecimal, BigInteger, byte, short, int, long and the respective wrappers of the primitive types; Additionally supported by HV: any sub-type of CharSequence (the numeric value represented by the character sequence is evaluated), any sub-type of NumberChecks whether the annotated value is less than or equal to the specified maximumAdds a check constraint on the column
@Min(value=)BigDecimal, BigInteger, byte, short, int, long and the respective wrappers of the primitive types; Additionally supported by HV: any sub-type of CharSequence (the numeric value represented by the char sequence is evaluated), any sub-type of NumberChecks whether the annotated value is higher than or equal to the specified minimumAdds a check constraint on the column
@NotNullAny typeChecks that the annotated value is not null.Column(s) are not nullable
@NullAny typeChecks that the annotated value is nullNone
@Pastjava.util.Date, java.util.Calendar; Additionally date/time API is on the class path: any implementations of ReadablePartial and ReadableInstantChecks whether the annotated date is in the pastNone
@Pattern(regex=, flag=)CharSequenceChecks if the annotated string matches the regular expression regex considering the given flag matchNone
@Size(min=, max=)CharSequence, Collection, Map and arraysChecks if the annotated element's size is between min and max (inclusive)Column length will be set to max
@ValidAny non-primitive typePerforms validation recursively on the associated object. If the object is a collection or an array, the elements are validated recursively. If the object is a map, the value elements are validated recursively.None

另外,Hibernate提供了额外的注解:

AnnotationSupported data typesUseHibernate metadata impact
@CreditCardNumber(ignoreNonDigitCharacters=)CharSequenceChecks that the annotated character sequence passes the Luhn checksum test. Note, this validation aims to check for user mistakes, not credit card validity! See also Anatomy of Credit Card Numbers. ignoreNonDigitCharacters allows to ignore non digit characters. The default is false.None
@EANCharSequenceChecks that the annotated character sequence is a valid EAN barcode. type determines the type of barcode. The default is EAN-13.None
@EmailCharSequenceChecks whether the specified character sequence is a valid email address. The optional parameters regexp and flags allow to specify an additional regular expression (including regular expression flags) which the email must match.None
@Length(min=, max=)CharSequenceValidates that the annotated character sequence is between min and max includedColumn length will be set to max
@LuhnCheck(startIndex=, endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=)CharSequenceChecks that the digits within the annotated character sequence pass the Luhn checksum algorithm (see also Luhn algorithm). startIndex and endIndex allow to only run the algorithm on the specified sub-string. checkDigitIndex allows to use an arbitrary digit within the character sequence as the check digit. If not specified it is assumed that the check digit is part of the specified range. Last but not least, ignoreNonDigitCharacters allows to ignore non digit characters.None
@Mod10Check(multiplier=, weight=, startIndex=, endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=)CharSequenceChecks that the digits within the annotated character sequence pass the generic mod 10 checksum algorithm. multiplier determines the multiplier for odd numbers (defaults to 3), weight the weight for even numbers (defaults to 1). startIndex and endIndex allow to only run the algorithm on the specified sub-string. checkDigitIndex allows to use an arbitrary digit within the character sequence as the check digit. If not specified it is assumed that the check digit is part of the specified range. Last but not least, ignoreNonDigitCharacters allows to ignore non digit characters.None
@Mod11Check(threshold=, startIndex=, endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=, treatCheck10As=, treatCheck11As=)CharSequenceChecks that the digits within the annotated character sequence pass the mod 11 checksum algorithm. threshold specifies the threshold for the mod11 multiplier growth; if no value is specified the multiplier will grow indefinitely. treatCheck10As and treatCheck11As specify the check digits to be used when the mod 11 checksum equals 10 or 11, respectively. Default to X and 0, respectively. startIndex, endIndex acheckDigitIndex and ignoreNonDigitCharacters carry the same semantics as in @Mod10Check.None
@NotBlankCharSequenceChecks that the annotated character sequence is not null and the trimmed length is greater than 0. The difference to @NotEmpty is that this constraint can only be applied on strings and that trailing whitespaces are ignored.None
@NotEmptyCharSequence, Collection, Map and arraysChecks whether the annotated element is not null nor emptyNone
@Range(min=, max=)BigDecimal, BigInteger, CharSequence, byte, short, int, long and the respective wrappers of the primitive typesChecks whether the annotated value lies between (inclusive) the specified minimum and maximumNone
@SafeHtml(whitelistType=, additionalTags=, additionalTagsWithAttributes=)CharSequence

Checks whether the annotated value contains potentially malicious fragments such as <script/>. In order to use this constraint, the jsoup library must be part of the class path.

With the whitelistType attribute a predefined whitelist type can be chosen which can be refined via additionalTags or additionalTagsWithAttributes. The former allows to add tags without any attributes, whereas the latter allows to specify tags and optionally allowed attributes using the annotation @SafeHtml.Tag.

None
@ScriptAssert(lang=, script=, alias=)Any typeChecks whether the given script can successfully be evaluated against the annotated element. In order to use this constraint, an implementation of the Java Scripting API as defined by JSR 223 ("Scripting for the JavaTM Platform") must part of the class path. The expressions to be evaluated can be written in any scripting or expression language, for which a JSR 223 compatible engine can be found in the class path.None
@URL(protocol=, host=, port= regexp=, flags=)CharSequenceChecks if the annotated character sequence is a valid URL according to RFC2396. If any of the optional parameters protocol, host or port are specified, the corresponding URL fragments must match the specified values. The optional parameters regexp and flags allow to specify an additional regular expression (including regular expression flags) which the URL must match.None

你可以通过下面的代码校验带注解的Java Bean实例。

1
2
3
4
5
6
Validator validator = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory()
.getValidator();
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( anInstance );

在Spring中可以配置如下:

1
2
3
4
5
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass"
value="org.hibernate.validator.HibernateValidator" />
</bean>

Apache BVal提供了JSR 303另外一个实现。

1
2
3
4
ValidatorFactory avf =
Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory();
Validator validator = avf.getValidator();
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( anInstance );

在Spring中可以配置如下:

1
2
3
4
5
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass"
value="org.apache.bval.jsr303.ApacheValidationProvider" />
</bean>

另外Apache还有一个较老的项目:Apache Commons Validator,提供了一些校验类。

1
2
3
4
5
6
7
8
DateValidator validator = DateValidator.getInstance();
// Validate/Convert the date
Date fooDate = validator.validate(fooString, "dd/MM/yyyy");
if (fooDate == null) {
// error...not a valid date
return;
}

依照carinae.net网站(http://carinae.net/2010/06/benchmarking-hibernate-validator-and-apache-beanvalidation-the-two-jsr-303-implementations/)的2010年的测试,
Apache bval性能要好于hibernate。 这么多年过去了,不知道现在的性能怎么样了。
性能比较