Bean验证和JSR 303

在本文中,我将向您展示如何使用新的Bean验证框架(又名JSR-303)。

遗产

在得到JSR 303(又名Bean验证框架)的结果之前,有两次有趣的尝试尝试验证框架。 每一个都来自层的末端,并专注于其范围。

前端验证

Struts是2001-2002年在表示层上学习和使用框架。 Struts使用MVC模型并专注于Controller,在带有Action Struts中表示。 视图是普通的JSP,Struts使用ActionForm以便将数据从Controller传递到View,反之亦然。 简而言之,这些是框架用来与View交互的POJO。

作为表示层框架,Struts关注的是验证用户输入。 动作表单有一个漂亮的方法,称为validate() 。 此方法的签名如下:

publicActionErrorsvalidate(ActionMappingmapping,HttpServletRequestrequest)

开发人员必须检查表单是否有效,然后填写(如果不是) ActionErrors对象(基本上是List )。 然后,如果ActionErrors对象不为空,Struts会将流重定向到错误页面(输入)。 由于手动检查很无聊且容易出错,因此使这种验证自动化可能是一个好主意。 即使在那时,声明式验证也被认为是关键。

Commons Validator徽标

这是Apache Commons Validator的目标。 其配置通过XML进行。 您指定:

  • 您有权访问的验证器。 有一些内置的,但您可以添加自己的
  • bean和验证器之间的关联:将通过哪些规则验证哪些bean

尽管Struts紧密集成了Commons Validator,但是您可以完全单独使用后者。 但是,最新的稳定版本(1.3.1)于2006年底发布。当前开发的版本是1.4,但是Maven网站自2008年初以来没有进行过更新。我的做法有些遗漏,因此我将其排除在外当我被迫使用Struts时,可以节省我的验证需求。

在这种情况下,我必须使用它,因为Struts插件知道如何使用两个XML配置文件来生成JavaScript客户端验证。

后端验证

以前,我们看到第一个验证框架来自用户输入。 另一方面,由于在数据库中强制执行了约束,因此插入/更新数据不需要进行此类验证。 例如,尝试将50个字符长的字符串插入VARCHAR(20)列将失败。

但是,让数据库处理验证有两个主要缺点:

  • 由于您需要连接到数据库,发送请求并处理错误,因此会降低性能
  • 这种错误不能轻易地映射到Java异常,如果可能的话,也可以映射到错误的特定属性

休眠验证器

最后,在将数据发送到数据库之前,最好在Java世界中验证域模型。 这就是Hibernate Validator的范围。 Commons Validator配置基于XML,而Hibernate Validator配置基于Java 5注释。

即使Hibernate Validator旨在验证域模型,您也可以使用它来验证任何bean。

JSR 303 Bean验证

最终,JSR 303取得了成果。 有两个重要的事实:它是不可知的,这意味着您可以在任何喜欢的地方使用它(前端,后端甚至是DTO(如果您遵循此模式)),并且其参考实现是Hibernate Validator v4。

JSR 303的功能包括:

  • 在两个不同级别上进行验证:属性或整个bean。 Hibernate Validator不可能做到这一点(因为它是面向数据库的),而Commons Validator却有很多限制
  • i18n准备就绪,消息已参数化
  • 可使用您自己的验证器扩展
  • 可使用注释或XML进行配置。 在下面,将仅显示注释配置

在JSR 303中,验证是以下各项之间交互作用的结果:

  • 注释本身。 有些是JSR 303随附的,但是您可以构建自己的
  • 将验证带注释的bean的类

最简单的例子

最简单的示例可能包括在类的属性上设置非空约束。 这样做很简单:

publicclassPerson{

  privateStringfirstName;

  @NotNull
  publicStringgetFirstName(){
    returnfirstName;
  }

  // setter
}

请注意,可以将@NotNull批注放置在属性或getter上(就像在JPA中一样)。 如果使用Hibernate,它也可以使用JSR 303注释来创建/更新数据库模式。

现在,为了验证此bean的实例,您要做的就是:

Set<ConstraintViolation<Person>>violations=validator.validate(person);

如果集合为空,则验证成功,否则,验证失败:其原理与之前的两个框架非常相似。

有趣的是,规范强制要求约束被继承。 因此,如果User类继承自Person ,则其firstName属性也将具有非null约束。

约束组

在表示层上,您可能不得不在两个不同的上下文(例如创建和更新)中使用相同的Form bean。 在这两种情况下,您都有不同的约束。 例如,创建个人资料时,用户名是必填项。 更新时无法更改,因此无需验证。

Struts(及其忠实的Commons Validator)通过将验证规则与Java类而不是映射相关联来解决此问题,因为它的范围是前端。 使用注释时,这是不可能的。 为了简化bean的重用,JSR 303引入了约束分组。 如果未指定任何内容(如前所述),则约束将分配给默认组,而在验证时,将在默认组中进行分配。

您还可以按如下约束指定组:

publicclassPerson{

  privateStringfirstName;

  @NotNull(groups=DummyGroup)
  publicStringgetFirstName(){
    returnfirstName;
  }
  // setter
}

因此,这将验证:

Personperson=newPerson();
// Empty set
Set<Constraintviolation<Person>>violations=validator.validate(person);

这还将:

Personperson=newPerson();
// Empty set
Set<Constraintviolation<Person>>violations=validator.validate(person,Default.class);

而且这不会:

Personperson=newPerson();
// Size 1 set
Set<Constraintviolation<Person>>violations=validator.validate(person,DummyGroup.class);

自定义约束

完成内置约束(和Hibernate扩展)的使用后,您可能需要开发自己的约束。 这很简单:约束是使用@Constraint注释的注释。 让我们创建一个约束来检查未大写的字符串:

@Target({METHOD,FIELD,ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy=CapitalizedValidator.class)
public@interfaceCapitalized{

  Stringmessage()default"{ch.frankel.blog.validation.constraints.capitalized}";
  Class<?>[]groups()default{};
  Class<?extendsPayload>[]payload()default{};
}

这三个元素分别用于国际化,分组(请参见上文)和传递元数据。 这些都是强制性的:如果未定义,则该框架将无法工作! 也可以添加更多元素,例如参数化验证: @Min@Max约束使用此元素。

注意,没有什么可以阻止将约束应用于实例而非属性,这是由@Target定义的,这是一种设计选择。

接下来是验证类。 它必须实现ConstraintValidator<?,?>

publicclassCapitalizedValidatorimplementsConstraintValidator<Capitalized,String>{

  publicvoidinitialize(Capitalizedcapitalized){}

  publicbooleanisValid(Stringvalue,ConstraintValidatorContextcontext){
    returnvalue==null||value.equals(WordUtils.capitalizeFully(value));
  }
}

就这样! 您现在要做的就是使用@Capitalized注释属性,并使用框架验证实例。 无需注册新创建的验证器。

约束构成

鼓励创建简单的约束,然后组合它们以创建更复杂的验证规则。 为了做到这一点,创建一个新的约束,并用您要组成的约束对其进行注释。 让我们创建一个约束,该约束将验证String既不为null也不不大写:

@NotNull
@Capitalized
@Target({METHOD,FIELD,ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy={})
public@interfaceCapitalizedNotNull{

  Stringmessage()default"{ch.frankel.blog.validation.constraints.capitalized}";
  Class<?>[]groups()default{};
  Class<?extendsPayload>[]payload()default{};
}

现在,用它注释属性,并观看魔术的发生!

当然,如果要防止约束组合,则必须限制@Target值以排除ANNOTATION_TYPE

结论

本文仅涉及JSR 303的表面。不过,我希望它是对它的功能的很好的介绍,并希望您有进一步研究它的愿望。

您可以在此处找到Eclipse / Maven格式的本文的源(以及更多)。

翻译自: https://blog.frankel.ch/bean-validation-and-jsr-303/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值