http://developer.51cto.com/art/201104/253257_1.htm

ava EE 6核心特征:Bean Validation特性概述(2)

2011-04-02 14:33 张冠楠 陈志娴 IBM developerWorks 字号:T | T

数据验证在 Java 分层结构的应用开发中占据着重要位置。Java EE 6 提出了 Bean Validation 规范,使用注解的方式对 Java Bean 进行约束验证,不局限于某一层次或者某一编程模型,灵活易用。本文将向您系统的介绍该规范的各种特性。

AD:WOT2015 互联网运维与开发者大会 热销抢票

约束的定义

约束注解

Bean Validation 规范对约束的定义包括两部分,一是约束注解,清单 1 中的 @NotNull 就是约束注解;二是约束验证器,每一个约束注解都存在对应的约束验证器,约束验证器用来验证具体的 Java Bean 是否满足该约束注解声明的条件。

在 Java Bean 中,对某一方法、字段、属性或其组合形式等进行约束的注解,即为约束注解,如清单 2 所示:

清单 2:

  1. @NotNull(message = "The id of employee can not be null")
  2. private Integer id;

清单 2 的含义为:对于字段 id,在 Java Bean 的实例中值不能为空。对于每一个约束注解,在实际使用前必须有相关定义。JSR303 规范默认提供了几种约束注解的定义(见表 1),我们也可以扩展规范提供的 API,实现符合自身业务需求的约束注解。

表 1. Bean Validation 规范内嵌的约束注解定义

约束注解名称 约束注解说明
@Null 验证对象是否为空
@NotNull 验证对象是否为非空
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMin 验证 Number 和 String 对象是否大等于指定的值,小数存在精度
@DecimalMax 验证 Number 和 String 对象是否小等于指定的值,小数存在精度
@Size 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Digits 验证 Number 和 String 的构成是否合法
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则

约束注解和普通的注解一样,一个典型的约束注解的定义应该至少包括如下内容(清单 3):

清单 3:

  1. @Target({ })   // 约束注解应用的目标元素类型
  2. @Retention()   // 约束注解应用的时机
  3. @Constraint(validatedBy ={})  // 与约束注解关联的验证器
  4. public @interface ConstraintName{
  5. String message() default " ";   // 约束注解验证时的输出消息
  6. Class[] groups() default { };  // 约束注解在验证时所属的组别
  7. Classextends Payload>[] payload() default { }; // 约束注解的有效负载
  8. }

约束注解应用的目标元素类型包括 METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER。METHOD 约束相关的 getter 方法;FIELD 约束相关的属性;TYPE 约束具体的 Java Bean;ANNOTATION_TYPE 用在组合约束中;该规范同样也支持对参数(PARAMETER)和构造器(CONSTRUCTOR)的约束。

验证时的组别属性将在本文第三大部分中组与组序列中详细介绍。

有效负载通常用来将一些元数据信息与该约束注解相关联,常用的一种情况是用负载表示验证结果的严重程度。

清单 4 给出一个验证字符串非空的约束注解的定义:

清单 4:

  1. @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
  2. @Retention(RUNTIME)
  3. @Documented
  4. @Constraint(validatedBy = {NotEmptyValidator.class})
  5. public @interface NotEmpty {
  6. String message() default "this string may be empty";
  7. Class[] groups() default { };
  8. Classextends Payload>[] payload() default {};
  9. }

约束注解定义完成后,需要同时实现与该约束注解关联的验证器。约束验证器的实现需要扩展 JSR303 规范提供的接口 javax.validation.ConstraintValidator。清单 5 给出该接口。

清单 5:

  1. public interface ConstraintValidator<a < span="">extends Annotation, T> {
  2. void initialize(A constraintAnnotation);
  3. boolean isValid(T value, ConstraintValidatorContext context);
  4. }

该接口有两个方法,方法 initialize 对验证器进行实例化,它必须在验证器的实例在使用之前被调用,并保证正确初始化验证器,它的参数是约束注解;方法 isValid 是进行约束验证的主体方法,其中 value 参数代表需要验证的实例,context 参数代表约束执行的上下文环境。

对于清单 4 定义的约束注解,清单 6 给出了与该注解对应的验证器的实现。

清单 6:

  1. public class NotEmptyValidator implements ConstraintValidator<notempty, string>{
  2. public void initialize(NotEmpty parameters) {
  3. }
  4. public boolean isValid(String string,
  5. ConstraintValidatorContext constraintValidatorContext) {
  6. if (string == null) return false;
  7. else if(string.length()<1) return false;
  8. else return true;
  9. }
  10. }

至此,一个可以声明并使用的约束注解已经定义完毕,清单 7 将给出该约束注解在实际程序中的使用。为节省篇幅,这里只给出针对清单 1 的增加和修改内容,未给出全部的示例代码,您可以在本文的附录中获得全部的代码。

清单 7:

首先在清单 1 中的类 Employee 中加入字段 company 和相应的 getter 和 setter 方法:

  1. @NotEmpty
  2. private String company;

然后在 main 函数中加入如下代码清单:

  1. String company = new String();
  2. employee.setCompany(company);

再次运行该程序,输出结果为:

  1. The id of employee can not be null
  2. this string may be empty
  3. The size of employee's name must between 1 and 10

 多值约束

下面介绍 Bean Validation 规范的一个特性,多值约束(Multiple Constraints):对于同一个目标元素,在进行约束注解声明时可以同时使用不同的属性达到对该目标元素进行多值验证的目的。如清单 8 所示:

清单 8:

  1. public @interface ConstraintName{
  2. String message() default " ";
  3. Class[] groups() default { };
  4. Classextends Payload>[] payload() default { };
  5. @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
  6. @Retention(RUNTIME)
  7. @Documented
  8. @interface List {
  9. ConstraintName[] value();
  10. }
  11. }

实现多值约束只需要在定义约束注解的同时定义一个 List(@interface List{})。使用该约束注解时,Bean Validation 将 value 数组里面的每一个元素都处理为一个普通的约束注解,并对其进行验证,所有约束条件均符合时才会验证通过。

清单 9 定义了一个约束注解,它用来验证某一字符串是否包含指定的内容。

清单 9:

  1. @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
  2. @Retention(RUNTIME)
  3. @Documented
  4. @Constraint(validatedBy = PatternOfStringValidator.class)
  5. public @interface PatternOfString {
  6. String mustContainLetter();
  7. String message() default "this pattern may not be right";
  8. Class[] groups() default { };
  9. Classextends Payload>[] payload() default {};
  10. @Target({ METHOD, FIELD, ANNOTATION_TYPE})
  11. @Retention(RUNTIME)
  12. @interface List {
  13. PatternOfString[] value();
  14. }
  15. }

该约束注解对应的验证器如清单 10 所示:

 清单 10:

  1. public class PatternOfStringValidator implements ConstraintValidator
  2. {
  3. private String letterIn;
  4. public void initialize(PatternOfString parameters) {
  5. this.letterIn=parameters.mustContainLetter();
  6. }
  7. public boolean isValid(String string,
  8. ConstraintValidatorContext constraintValidatorContext) {
  9. if (string.contains(letterIn))
  10. return true;
  11. return false;
  12. }
  13. }

如果想验证某一字符串是否同时包含两个子串,那么多值约束就显得比较重要了,清单 11 将详细给出多值约束的使用。

清单 11:

在清单 1 中的类 Employee 中增加如下字段 place 以及相应的 getter 和 setter 方法:

  1. @PatternOfString.List({
  2. @PatternOfString(mustContainLetter = "CH",
  3. message = "It does not belong to China"),
  4. @PatternOfString(mustContainLetter="MainLand",
  5. message="It does not belong to MainLand")})
  6. private String place;

然后在 main 函数中加入如下代码清单:

  1. String place = "C";
  2. employee.setPlace(place);

再次运行该程序,输出结果为:

  1. It does not belong to MainLand
  2. It does not belong to China
  3. this string may be empty
  4. The id of employee can not be null
  5. The size of employee's name must between 1 and 10

如果将 place 赋值为 String place = "CHINA",则输出结果为:

  1. this string may be empty
  2. The id of employee can not be null
  3. It does not belong to MainLand
  4. The size of employee's name must between 1 and 10

可见,该约束会对声明的两个约束注解分别进行验证,只要存在不符合约束验证规则的 Java Bean 实例,就将产生相应的验证失败信息。约束注解声明的时候可以根据不同的约束值使用 message 参数给出不同的输出信息。

组合约束

下面介绍 Bean Validation 规范中另一个重要的特性:组合约束。Bean Validation 规范允许将不同的约束进行组合来创建级别较高且功能较多的约束,从而避免原子级别约束的重复使用。如清单 4 定义的约束注解 @NotEmpty,是用来判断一个字符串在非空的基础上长度至少为 1,其实际意义等同于 @NotNull 和 @Size(min=1)的组合形式,因此可以将 @NotEmpty 约束定义为组合约束 NotEmpty2,如清单 12 所示:

清单 12:

  1. @NotNull
  2. @Size(min = 1)
  3. @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
  4. @Retention(RUNTIME)
  5. @Documented
  6. @Constraint(validatedBy = {NotEmptyValidator2.class})
  7. public @interface NotEmpty2 {
  8. String message() default "this string may be empty";
  9. Class[] groups() default { };
  10. Classextends Payload>[] payload() default {};
  11. @Target({ METHOD, FIELD, ANNOTATION_TYPE})
  12. @Retention(RUNTIME)
  13. @interface List {
  14. NotEmpty2[] value();
  15. }
  16. }

java validator的原理与使用的更多相关文章

  1. 深入Java核心 Java内存分配原理精讲

    深入Java核心 Java内存分配原理精讲 栈.堆.常量池虽同属Java内存分配时操作的区域,但其适用范围和功用却大不相同.本文将深入Java核心,详细讲解Java内存分配方面的知识. Java内存分 ...

  2. paip.java UrlRewrite 的原理and实现 htaccess正则表达式转换

    paip.java UrlRewrite 的原理and实现 htaccess正则表达式转换 #---KEYWORD #-正则表达式 正则表达式 表示 非指定字符串开头的正则 排除指定目录.. 作者 老 ...

  3. Java虚拟机工作原理详解 (一)

    一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 javac YourClassNam ...

  4. Java虚拟机工作原理详解

    原文地址:http://blog.csdn.net/bingduanlbd/article/details/8363734 一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了 ...

  5. Java爬虫搜索原理实现

    permike 原文 Java爬虫搜索原理实现 没事做,又研究了一下爬虫搜索,两三天时间总算是把原理闹的差不多了,基本实现了爬虫搜索的原理,本次实现还是俩程序,分别是按广度优先和深度优先完成的,广度优 ...

  6. Java虚拟机工作原理具体解释

    一.类载入器 首先来看一下java程序的运行过程. 从这个框图非常easy大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘其中.然后你在命令行中输入 javac YourClass ...

  7. Java基础知识强化之多线程笔记05:Java程序运行原理 和 JVM的启动是多线程的吗

    1. Java程序运行原理:     Java 命令会启动Java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程.该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 m ...

  8. Java validator整理

    Java validator整理 因为想对方法的入参和出参作简单的非空或者非空字符做校验,所以找了下相关的@NotNull注解 类 | 说明 --- | --- javax.validation.co ...

  9. Java环境配置原理

    Java环境配置原理详解 1.Jdk安装目录文件说明: 一般jdk安装目录及路径 \Java\jdk1.7.0_79\lib,里面主要包含以下文件夹. bin:主要存放的是java工具中常用命令如:j ...

随机推荐

  1. maven的pom报plugins却是的解决方法2

    Failure to transfer org.apache.maven.plugins:maven-plugins:pom:25 from https://repo.maven.apache.org ...

  2. HDU 3586-Information Disturbing(树形dp)

    题意: n个节点的通信连接树,切断每个边有一定的花费,要你切断边,在总花费不超过m的前提,使所有的其他节点都不能和节点1(根)连通,切边时有花费上限,让你最小化这个上限. 分析:最小化最大值,想到二分 ...

  3. POJ 1236 Network of Schools 有向图强连通分量

    参考这篇博客: http://blog.csdn.net/ascii991/article/details/7466278 #include <stdio.h> #include < ...

  4. 动态加载JS(css)文件

    <script language="javascript">document.write("<script src='test.js'><\ ...

  5. 关于java环境配置的问题

    在以前刚开始接触Java环境配置问题的时候,配置了一大串的字符串,所有的路径全部在一个path变量里面,特别冗杂. 由于这两天重新再装了一个版本的JDK结果出现毛病了,就只有重新配置该环境变量,结果又 ...

  6. php中的字符串和正则表达式

    一.字符串类型的特点 1.PHP是弱类型语言,其他数据类型一般都可以直接应用于字符串函数操作. 1: <?php //输出345 //输出345 //先查找hello常量,若没找到,将hello ...

  7. android-serialport-api and libusb for android

    libusb for android: Even if you get it compiled, Android is probably not going to let you access the ...

  8. Adding an On/Off switch to your Raspberry Pi

    http://www.raspberry-pi-geek.com/Archive/2013/01/Adding-an-On-Off-switch-to-your-Raspberry-Pi#articl ...

  9. 总结2015搭建日志,监控,ci,前端路由,数据平台,画的图与界面 - hugo - ITeye技术网站

    总结2015搭建日志,监控,ci,前端路由,数据平台,画的图与界面 - hugo - ITeye技术网站 极分享:高质分享+专业互助=没有难做的软件+没有不得已的加班 极分享:高质分享+专业互助=没有 ...

  10. 网吧局域网里的设置外网IP地址、设置内网IP地址、限制内网速度和路由器共享

    现在啊,网吧的需求越来越高,同时在经济比较充裕的情况下,作为网吧的老板可能希望打造全千兆的网吧,让每个进入网吧的人都能充分体验高速的感觉,当然更重要的是在同行竞争中处于上游,特别是对网络游戏爱好者的吸 ...