写点什么

Emmanuel Bernard 谈 Bean 验证规范

  • 2008-07-15
  • 本文字数:4956 字

    阅读完需:约 16 分钟

接着之前一篇关于 Bean 验证框架草案的文章,InfoQ 有幸采访到了Emmanuel Bernard,向其了解了专家组正在寻求的建议和社区参与的更多相关信息。

InfoQ:我之前很期望规范能支持某种 XML 模型和 Annotation,但规范似乎没有。这些内容你是不是已经移除了呢?

Emmanuel Bernard (EB):规范将支持 XML 模型。不过我们最初是想先解决功能集和元模型,然后再仔细检查 XML 描述符的处理过程。Annotation 可用做 JSR 303 Bean 验证实现中的内部模型。一旦我们正确定义了 Annotation 模型,我们就无需再使用 XML 了,这仅仅是另一种语言。

InfoQ:你们打算支持错误消息的层次吗?——这样,我能用标准的方式显示一个通用的错误消息,如果我愿意,我可以用页面 / 屏幕特定的错误消息覆盖那个通用的错误消息。

EB:我不是很确定我理解了你的问题,但是让我从两个角度来试着回答一下
a)Bean 验证规范能让你通过基于 ResourceBundle 本地化的标准文件系统来外部化和国际化约束错误消息。约束可以声明一个关键字,而不是使用“硬编码”的消息。

@Length(max = 50, message="{constraint.name.tooLong}")<br></br> String name;<p><em>## French resourceBundle constraint.name.tooLong = </em></p>Le nom ne peut dépasser {max} caractères 为了超越这一模式、提供一种与应用或展示框架(比如 Web 框架)的自然集成,该消息解决策略完全是可插拔的。

让我们举一个例子:Web Beans 促进了上下文相关组件(比如事件、页面、请求、会话、业务流程)的思想。Web Beans 可以利用 Bean 验证,并插入其上下文知道的消息解决策略:包含上下文相关表达式的错误消息接着会被处理。

另一个例子是,Wicket 的资源包解决方案是分层定义的。它首先尝试在面板资源文件中查找关键字,然后是页面资源文件,最后才是应用资源文件。遵照这一模式,Wicket 能提供它自己的消息解决实现。

在这两种情况下,对提供合适的错误消息来说,上下文相关的信息至关重要。Bean 验证调用者处于辨别该上下文更好的位置上:通过提供一个自定义的消息解决实现,在仍然能将验证工作委派给 Bean 验证的同时,Bean 验证调用者还能给使用者提供一个自然、同源的体验。

b)更为理性地说,我想知道你为什么需要改变某个特定页面的约束消息呢?一条约束消息需要在特定上下文中“自定义”通常是因为,看似是一个约束的内容,实际上是两个适用于不同上下文的不同约束。Bean 验证规范提供了描述上下文相关约束的机制。每个约束属于一或多个组。验证对象的时候,会列出一个组列表,以供对使用验证的上下文进行选择。我在一篇博文中描述了一些适用于组的用例。

InfoQ:国际化和验证有一个共同的问题,就是不同地区之间验证规则可能会有所不同。比如字母数字的测试可能会有不同。这该如何处理呢?

EB:我通常看到的是,约束规则对属性值的依赖要比对实际地区的依赖要多。为了举一个地址的规范例子,邮编或电话号码约束会根据该地址所属的国家不同而不同。

一些约束不会有变化(非空、数据库中的最大长度),但有一些会因国家而不同。规范现在通过提供类级别的约束处理了这一问题。类级别的约束能获取 Bean 实例,而不是它的某一属性,还能应用一些基于若干属性的约束。电话号码验证逻辑可以适用于法国和美国的不同规则,它甚至可以为加拿大的电话号码调用外部服务,加拿大的电话号码基于已存在的数据库,而不是格式化的规则。

这是一个有趣的问题,人们可以对应该有什么解决办法而有不同的尝试。类级别的方法提供了最大的灵活性,并保持了规范的简单性。争论仍然很激烈:如果你有什么意见,可以到 http://forum.hibernate.org/viewforum.php?f=26 发言:)

InfoQ:我们能使用表达式语言(Expression Language)巧妙地处理错误消息吗?或者说我们有消息的插入点吗(在{x}和{y}中间插入一个数字)?

EB:是的,缺省的消息处理策略允许约束参数的注入。

@Length(max = 50, message="{constraint.name.tooLong}")<br></br> String name;<br></br> constraint.name.tooLong = Names must not be longer than {max} characters上面这段内容会得到消息:名称不得超过 50 个字符。

但这显然是专家组正在寻求反馈和建议的领域。在 Bean 验证反馈论坛上已经开始了一些讨论,以便提供额外的上下文相关信息。

而且正如我先前描述的那样,自定义消息解决策略可以提供给 Bean 验证提供商:这是提供与现有 Web 和应用框架自然集成的关键。我认为应用和 Web 框架在它们处理方式上的信息要比 Bean 验证框架更多,所以它们能提供一个更好的变量替换和表达式语言解决办法。

InfoQ:你打算支持错误消息中的超链接吗?如果有的话,本地化问题该如何处理(比如链接的位置在不同的地区可能会不同)?

EB你所描述的问题对展示框架的依赖性很强。展示框架提供的自定义消息解决策略能完美地处理这种情况,因为演示框架有所有的上下文相关信息(URL 根路径、传递的参数等)。

InfoQ:层之间的多语言情景该如何处理?比如说,如果后端系统用一种语言(假定是英语)生成了错误消息,但我需要用另一种语言显示给用户,如粤语。

EB:这是一个非常有趣的问题,可惜我不知道所有的解答,因为它涉及整个 Java EE 平台,而不仅仅是 Bean 验证。
如果期望用粤语读取,后端系统就不应该用英语生成错误消息。但规范还没有描述这个问题,专家组期望能提供自定义地区的能力,用于特定的验证调用(可能是通过基于传递模型的线程)。应用框架将负责把地区从客户端传到服务器端。

正如你看到的,集成应用框架(调用者)和 Bean 验证将会是提供极好的用户体验的关键。不幸的是(或许也是幸运的),这个 JSR 侧重于约束声明和验证。但是专家组得意识到大局,我们尽量提供必要的扩展和集成点,使使用者能适当地集成。

InfoQ:我希望理想的情况是能用同一个框架处理我所有的验证(假定从 JavaScript 开始,在应用层,针对数据库)。目前规范离这一目标有多近呢?

EB跨越这些异构层共享相同的约束定义,我认为这一能力才是至关重要的。如果相同的验证框架能被重用,就更好了。

Bean 验证是层不可知的,上至展示层、下到 DAO 层,所有这些 Java 层都可以简单地将约束验证委派给 Bean 验证运行时。让我们举几个例子:

  • 每次实体被添加或更新到数据库时,Java 持久层可以调用 Bean 验证
  • 业务构件可以在应用核心业务逻辑之前调用一组数据的 Bean 验证
  • 展示层可以调用验证逻辑对表单提供的数据进行验证,然后再将该数据传递给域模型
  • 该上下文中的 Bean 验证是验证运行时引擎,由架构中的各部分进行调用。由于约束被放置在域模型类中、由各个层共享,所以可全面地应用同样的约束。

对 Java 范围之外的层来说,它变得更加复杂和有趣。数据库就是一个很好的例子。一些约束在数据库 Schema 中非常合理(非空、长度等等)。Bean 验证规范通过元数据请求 API 暴露一些约束的基本元数据信息。Java 持久化能钩到这个 API 上,提取有用的信息,并将一些约束应用于数据库 Schema。

在展示端,特别是在 Web 应用中,一些约束能被 JavaScript 代码使用。这减少了到服务器的数据传输次数,通过及早的错误反馈提供了更好的用户体验。在这种情况下有两个方法是可行的。展示 Widget(比如 JSF 组件)能从 Bean 验证请求 API 中提取约束元数据,并在 JavaScript 中使用相同的逻辑。当然不是所有的约束都能用这种模型来表述。尽管它们中的一些将用于客户端。该方法的漂亮之处是客户端和服务器端都能透明地保持一致性,就像它们都能共享来自域模型的约束声明一样。尽管我们不会重用实际的约束实现,但是元数据 API 提供一些方法来提取约束定义并重用它。第二种方法是使用类似 GWK 的方法:Java 代码转化为 JavaScript:相同的实现逻辑接着会在客户端和服务器端之间共享。

InfoQ:好——那么假设我在 Web 应用中有一个电话号码属性。该属性是必需的,最大长度是 40 个字符,而且只接受字母数字。我想在 Web 端使用 JavaScript 检查属性的长度。在数据库端,我也定义一个 CHAR40 字段。如果我想从 Web 层查找约束验证,我该如何去做?又能获得什么?你了解 JPA 2 中同样的工作方式吗?

EB你所描述的内容实质上引出了那些超出 Java 范围的约束。为了使这种情况发生,连接 Java 世界和 Web/JavaScript 工作的框架需要:

  • 知道 Web 表单输入和域模型属性之间的联系
  • 获取到约束描述

第一点是链接框架的任务。通过检查表达式语言(比如在 JSF 中)这一点就能实现,它能实现是因为这个框架使用“Java”(比如在 Wicket 中)。

第二点出现是因为链接框架可以从属性中查询 Bean 验证的元数据 API,并接受约束的列表。每个约束(不只实现了验证逻辑)描述了它如何能投映到一组预定义的静态约束范围内。这是什么意思呢?它用冗长的方式表明每一个约束描述是否:

  • 执行非空性、可空性。
  • 执行长度约束。
  • 执行最大值。

等等这些。这将要求展示框架中的一些工作包含并使用 Bean 验证 API,但它还是会给开发人员带来很多的价值。

与 Java Persistence 进行集成,和生成数据库 Schema 的方式大部分都将是相同的:仅仅用“列”替换“表单输入”就可以了。

InfoQ:规范受 Hibernate 验证框架的影响比较大,但是组的概念似乎不是。你能不能告诉我们这个主意是从哪里来的?你是怎么看着它被利用起来的?

EB:规范向 Hibernate 验证框架借鉴了很多,但也抓住了许多不同验证框架的思想。因此,减少规范中错误的最好方式就是基于现有的工作特性:事实上,规范中的大部分功能要么已经通过实现从头开始的概念进行了验证,要么已经通过对 Hibernate 验证框架的分支进行了验证,从而保证了它们的成功。

我不知道究竟在何时何地开始了组功能,所以如果我归功错的话,请原谅。我知道 Rifle 很久以前就有类似的功能了,Hibernate 验证框架的用户也要求这一功能。关于组和约束之间的依赖,一般来说比较难的事情是对它们的描述很快就会变得非常非常冗长和复杂。规范试图在灵活性和简单性之间找到平衡。我们在反馈论坛中已经尝试开始一些比较有趣的讨论,并改进它。

在下面几中情况下组会非常重要:

  • 定义约束的一个子集,以根据用例对其分组(用户是有效的,而且用户在一次点击中提供了足够的信息)
  • 验证数据,因为数据是由用户提供的:并非所有的数据都在同一时间内提供。某一特定的对象可能只有部分可验证
  • s 一些约束应该在另外一些之后再验证,而且只有前面的验证通过才能进行验证。这可能是因为时间或 CPU 的代价比较昂贵,或者是因为它们期望数据处于“合适的”状态。

组为这些用例提供了一个声明性的解决方案。我们期望 Java Persistence、JSF、Web Beans 或任何应用框架都能提供声明性方式去请求组特定的验证。

InfoQ:你如何处理有可能与其他专家组重叠的地方?比如说在 JPA2、Web Beans 等领域。

EB:严格来说,JSR 303 和你提到的规范没有重叠,但有一定的集成机会。尽管集成工作还没有正式开始,但是许多 303 专家组的成员同时也是 JPA 2、JSF 2 和 Web Beans 专家组的成员。我个人是 JPA 2 专家组的成员,已经对 Java Persistence 集成思索了一段时间。随着早期的草案发布,其他专家组可以评论并提供反馈,使集成成真。

InfoQ:花多长时间能让原型实现可用呢?

EB:实际上社区超越了我们。你可以找到一些已有的实现。我知道的有 http://code.google.com/p/agimatec-validation/ .
尽管不兼容(尤其是 API 方法),Hibernate 验证框架还是实现了规范基于的大部分概念,并全面探索了我们刚刚讨论的集成模型:

  • 它集成了 Hibernate Core,以同步数据库 Schema
  • 它通过标记与 JBoss Seam 和 JSF 进行了集成。在将表单属性传递到应用流程的剩余部分之前,标记触发了对表单属性的 Hibernate 验证的调用
  • 一些 UI Widget 重用了 Hibernate 验证框架的约束,并通过 JavaScript 调用将其应用到客户端

InfoQ:你们要从 Java 社区得到什么呢?

EB:我们非常盼望获得规范相关的反馈:

  • 它如何能更好得满足应用开发人员的需要(易于使用、用例)
  • 它如何能更好地满足框架开发人员的需要(集成点、重用性)
  • 它如何进行改进

我们已经放弃了一个基于电子邮件的信息反馈系统,开放了一个公开论坛 ,人们可以在那里互动、提问、提出不同的解决方案。这是修改和提高规范的完美时机。迄今为止,反馈都非常好并且很有趣,所以我们将继续进行:)

查看英文原文 Interview: Emmanuel Bernard on the Bean Validation specification

2008-07-15 12:411039
用户头像

发布了 151 篇内容, 共 61.8 次阅读, 收获喜欢 18 次。

关注

评论

发布
暂无评论
发现更多内容
Emmanuel Bernard谈Bean验证规范_Java_Charles Humble_InfoQ精选文章