JSR 303 - Bean Validation 介绍及最佳实践

JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案。2009 年 12 月 Java EE 6 发布,Bean Validation 作为一个重要特性被包含其中。本文将对 Bean Validation 的主要功能进行介绍,并通过一些示例来演示如何在 Java 开发过程正确的使用 Bean Validation。

关于 Bean Validation

在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。应用程序必须通过某种手段来确保输入进来的数据从语义上来讲是正确的。在通常的情况下,应用程序是分层的,不同的层由不同的开发人员来完成。很多时候同样的数据验证逻辑会出现在不同的层,这样就会导致代码冗余和一些管理的问题,比如说语义的一致性等。为了避免这样的情况发生,最好是将验证逻辑与相应的域模型进行绑定。

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

下载 JSR 303 – Bean Validation 规范 http://jcp.org/en/jsr/detail?id=303

Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。如果想了解更多有关 Hibernate Validator 的信息,请查看 http://www.hibernate.org/subprojects/validator.html

 

回页首

Bean Validation 中的 constraint

表 1. Bean Validation 中内置的 constraint
Constraint 详细信息
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
表 2. Hibernate Validator 附加的 constraint
Constraint 详细信息
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内

一个 constraint 通常由 annotation 和相应的 constraint validator 组成,它们是一对多的关系。也就是说可以有多个 constraint validator 对应一个 annotation。在运行时,Bean Validation 框架本身会根据被注释元素的类型来选择合适的 constraint validator 对数据进行验证。

有些时候,在用户的应用中需要一些更复杂的 constraint。Bean Validation 提供扩展 constraint 的机制。可以通过两种方法去实现,一种是组合现有的 constraint 来生成一个更复杂的 constraint,另外一种是开发一个全新的 constraint。

 

回页首

创建一个包含验证逻辑的简单应用(基于 JSP)

在本文中,通过创建一个虚构的订单管理系统(基于 JSP 的 web 应用)来演示如何在 Java 开发过程中应用 Bean Validation。该简化的系统可以让用户创建和检索订单。

系统设计和运用的技术

图 1. 系统架构

图 1 是报表管理系统的结构图,是典型的 MVC(Model-View-Controller)应用。Controller 负责接收和处理请求,Servlet 扮演 Controller 的角色去处理请求、业务逻辑并转向合适的 JSP 页面。在 Servlet 中对数据进行验证。JSP 扮演 View 的角色以图型化界面的方式呈现 Model 中的数据方便用户交互。Model 就是此系统进行操作的数据模型,我们对这部分加以简化不对数据进行持久化。

数据模型

图 2. 数据模型

图 2 展示的是订单管理系统的数据模型。

声明了 contraint 的 JavaBean

清单 1. Order.java
  1. public class Order {
  2. // 必须不为 null, 大小是 10
  3. @NotNull
  4. @Size(min = 10, max = 10)
  5. private String orderId;
  6. // 必须不为空
  7. @NotEmpty
  8. private String customer;
  9. // 必须是一个电子信箱地址
  10. @Email
  11. private String email;
  12. // 必须不为空
  13. @NotEmpty
  14. private String address;
  15. // 必须不为 null, 必须是下面四个字符串'created', 'paid', 'shipped', 'closed'其中之一
  16. // @Status 是一个定制化的 contraint
  17. @NotNull
  18. @Status
  19. private String status;
  20. // 必须不为 null
  21. @NotNull
  22. private Date createDate;
  23. // 嵌套验证
  24. @Valid
  25. private Product product;
  26.  

  27. getter setter
  28. }
清单 2. Product.java
  1. public class Product {
  2. // 必须非空
  3. @NotEmpty
  4. private String productName;
  5. // 必须在 8000 至 10000 的范围内
  6. // @Price 是一个定制化的 constraint
  7. @Price
  8. private float price;

  9. Getter setter
  10. }
清单 3. OrderQuery.java
  1. // 'to'所表示的日期必须在'from'所表示的日期之后
  2. // @QueryConstraint 是一个定制化的 constraint
  3. @QueryConstraint
  4. public class OrderQuery {
  5. private Date from;
  6. private Date to;
  7. omitted
  8. Getter and setter
  9. }

定制化的 constraint

@Price是一个定制化的 constraint,由两个内置的 constraint 组合而成。

清单 4. @Price 的 annotation 部分
  1. // @Max 和 @Min 都是内置的 constraint
  2. @Max(10000)
  3. @Min(8000)
  4. @Constraint(validatedBy = {})
  5. @Documented
  6. @Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface Price {
  9. String message() default "错误的价格";
  10. Class<?>[] groups() default {};
  11. Class<? extends Payload>[] payload() default {};
  12. }

@Status是一个新开发的 constraint.

清单 5. @Status 的 annotation 部分
  1. @Constraint(validatedBy = {StatusValidator.class})
  2. @Documented
  3. @Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
  4. @Retention(RetentionPolicy.RUNTIME)
  5. public @interface Status {
  6. String message() default "不正确的状态 , 应该是 'created', 'paid', shipped', closed'其中之一";
  7. Class<?>[] groups() default {};
  8. Class<? extends Payload>[] payload() default {};
  9. }
清单 6. @Status 的 constraint validator 部分
  1. public class StatusValidator implements ConstraintValidator<Status, String>{
  2. private final String[] ALL_STATUS = {"created", "paid", "shipped", "closed"};
  3. public void initialize(Status status) {
  4. }
  5. public boolean isValid(String value, ConstraintValidatorContext context) {
  6. if(Arrays.asList(ALL_STATUS).contains(value))
  7. return true;
  8. return false;
  9. }
  10. }
 

回页首

Bean Validation API 使用示例

创建订单

用户在创建一条订单记录时,需要填写以下信息:订单编号,客户,电子信箱,地址,状态,产品名称,产品价格

图 3. 创建订单

对这些信息的校验,使用 Bean Validation API

清单 7. 代码片段
  1. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  2. throws ServletException, IOException {
  3. HttpSession session = req.getSession();
  4. // 从 request 中获取输入信息
  5. String orderId = (String) req.getParameter("orderId");
  6. String customer = (String) req.getParameter("customer");
  7. String email = (String) req.getParameter("email");
  8. String address = (String) req.getParameter("address");
  9. String status = (String) req.getParameter("status");
  10. String productName = (String) req.getParameter("productName");
  11. String productPrice = (String) req.getParameter("productPrice");
  12. // 将 Bean 放入 session 中
  13. Order order = new Order();
  14. order.setOrderId(orderId);
  15. order.setCustomer(customer);
  16. order.setEmail(email);
  17. order.setAddress(address);
  18. order.setStatus(status);
  19. order.setCreateDate(new Date());
  20. Product product = new Product();
  21. product.setName(productName);
  22. if(productPrice != null && productPrice.length() > 0)
  23. product.setPrice(Float.valueOf(productPrice));
  24. order.setProduct(product);
  25. session.setAttribute("order", order);
  26. ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
  27. Validator validator = factory.getValidator();
  28. Set<ConstraintViolation<Order>> violations = validator.validate(order);
  29. if(violations.size() == 0) {
  30. session.setAttribute("order", null);
  31. session.setAttribute("errorMsg", null);
  32. resp.sendRedirect("creatSuccessful.jsp");
  33. } else {
  34. StringBuffer buf = new StringBuffer();
  35. ResourceBundle bundle = ResourceBundle.getBundle("messages");
  36. for(ConstraintViolation<Order> violation: violations) {
  37. buf.append("-" + bundle.getString(violation.getPropertyPath().toString()));
  38. buf.append(violation.getMessage() + "<BR>\n");
  39. }
  40. session.setAttribute("errorMsg", buf.toString());
  41. resp.sendRedirect("createOrder.jsp");
  42. }
  43. }

如果用户不填写任何信息提交订单,相应的错误信息将会显示在页面上

图 4. 验证后返回错误信息

其实在整个程序的任何地方都可以调用 JSR 303 API 去对数据进行校验,然后将校验后的结果返回。

清单 8. 调用 JSR 303 API 进行校验
  1. Order order = new Order();

  2. ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
  3. Validator validator = factory.getValidator();
  4. Set<ConstraintViolation<Order>> violations = validator.validate(order);

JSR 303 - Bean Validation 介绍及最佳实践的更多相关文章

  1. JSR 303 - Bean Validation 介绍及最佳实践(转)

    JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案.2009 年 12 月 Java EE 6 发布,Bean Validation 作为一个 ...

  2. JSR教程1——JSR 303 - Bean Validation介绍

    1.Bean Validation 在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情.应用程序必须通过某种手段来确保输入进来的数据从语义上来讲是正确的.在通常的情况下, ...

  3. JSR 303 - Bean Validation 是什么?

    关于 Bean Validation JSR 303 - Bean Validation 是jree6 中的一项子规范,JSR 303 - Bean Validation着重解决以下实际问题: 在任何 ...

  4. JSR 303 - Bean Validation 模型验证

    类是转载的,不知道转的哪里的. 此类依赖 JSR 303 – Bean Validation, Hibernate Validator. 代码不能直接运行.意会一下.自己改改. import com. ...

  5. JSR 303 - Bean Validation 简单介绍及用法

    一.JSR-303简单介绍 JSR-303 是 JAVA EE 6 中的一项子规范.叫做 Bean Validation,官方參考实现是Hibernate Validator. 此实现与 Hibern ...

  6. JSR 303 - Bean Validation 简介及使用方法

    参考:https://blog.csdn.net/xlgen157387/article/details/46848507 自己写的验证: /** * * @ClassName: BeanValida ...

  7. Spring MVC学习笔记——JSR303介绍及最佳实践

    JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案.2009 年 12 月 Java EE 6 发布,Bean Validation 作为一个 ...

  8. JSR-303 Bean Validation 介绍及 Spring MVC 服务端参数验证最佳实践

    任何时候,当要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情. 应用程序必须通过某种手段来确保输入参数在上下文来说是正确的. 分层的应用很多时候同样的数据验证逻辑会出现在不同的层,这样 ...

  9. JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

    任何时候,当要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情. 应用程序必须通过某种手段来确保输入参数在上下文来说是正确的. 分层的应用在很多时候,同样的数据验证逻辑会出现在不同的层, ...

随机推荐

  1. CentOS7 安装RabbitMQ

    第一.下载erlang和rabbitmq-server的rpm: http://www.rabbitmq.com/releases/erlang/erlang-19.0.4-1.el7.centos. ...

  2. 1.JavaScript面试

    1.1. 简要描述 JavaScript 的数据类型?   参考答案:   JavaScript 的数据类型可以分为原始类型和对象类型. 原始类型包括 string.number 和 boolean  ...

  3. c# 配置文件之configSections配置(三)

    使用IConfigurationSectionHandler配置configSections ·步骤1:定义一个实体类 using System; using System.Collections.G ...

  4. WaitForSingleObject 和 WaitForMultipleObjects函数

    1.WaitForSingleObject 等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止.这些等待函数中最常用的是WaitForSingleObject:   DWORD ...

  5. c#新手之1-如何组织类及相互调用

    不知道这个文章的名字起的对不对,姑且这么叫吧.我在这之前用c语言写程序几乎很少用函数调用来解决问题,都是用全局变量然后面向过程对数据做简单的处理,这就造成了我在学习c@之后仍有这个毛病,好点的时候有个 ...

  6. lamp环境搭配(centos6.4)

    (一)如果你的服务器没有链接网络可以先挂载本地光盘.设置yum源. 挂载光盘: [root@delphi ~]# mkdir /mnt/cdrom #新建挂载点 [root@delphi ~]# mo ...

  7. WAMP环境启动失败处理办法

    点击控制面板->系统与安全->管理工具->查看事件日志->windows日志->应用程序 查看错误日志,查找错误并解决

  8. iOS应用数据存储2-SQLite3数据库

          SQLite3 SQLite3是一款开源的嵌入式关系型数据库,可移植性好,易使用,内存开销小. SQLite3是无类型的,意味着你可以保存任何类型的数据到任意表的任意字段中. SQLite ...

  9. Mac上常用的一些命令

    FTP:先cd到要传的文件的文件夹>ftp 10.214.111.1cd到上传的ftp文件put 文件名 虚拟环境cd myproject. venv/bin/activate 激活sudo p ...

  10. CSS第一天总结

    CSS是层叠样式表,其作用在我看来就是统一一个或多个元素或者ID.class等的属性,CSS可以定义的属性非常多,一个好看的网页离不开CSS的修饰. CSS简而言之就是三个部分:选择符.属性.属性值. ...