- 浏览: 1055342 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (279)
- Apache net (10)
- JBoss Rules (3)
- Seam ACL (1)
- Seam (14)
- Seam JPA高级权限验证 (8)
- 待解决的问题.... (1)
- JAVA (43)
- Dwr (4)
- Ajax4JSF (1)
- JavaScript (27)
- 生活小常识 (17)
- Richfaces (3)
- seam自己经历 (14)
- JDBC (1)
- mysql (6)
- WebService (10)
- Java Web (4)
- Hibernate (13)
- J2EE框架整合 (3)
- Spring (9)
- BEA Weblogic (1)
- XML (1)
- log4j (6)
- CSS (2)
- javaIO文件的读写 (5)
- SVN服务器的安装 (5)
- powerDesigner (2)
- SQL常用语句 (3)
- wicket初学 (5)
- eclipse (7)
- 正则表达式 (1)
- ExtJS (6)
- maven(m2eclipse) (1)
- struts2.0 (9)
- JPA (6)
- struts2.0整合spring2.5 (9)
- linux (6)
- Oracle (5)
- Servlet (3)
- MyEclipseGen (0)
最新评论
-
qq_31247573:
JAVA 获取http返回XML的数据 -
jasmine_20100810:
...
linux下tomcat服务的启动、关闭与错误跟踪 -
weiaiFang0624:
视频下载地址:http://download.csdn.net ...
there is no action mapped for namespace / and action name解决办法 -
p476462534:
JS控制表单form的提交 -
dandongsoft:
aaaaaaaaaaaaaaa
httpClient,JAVA访问http request response
本文为Bean验证系列的第二部分。总体介绍请阅读这篇文章。本文主要介绍了约束定义。
可以通过内置约束(例如@NotNull、@Length等)来进行Bean验证,本文介绍的主要部分为基础验证的扩展。我们非常鼓励开发人员针对具体的业务需求编写自定义约束。
编写自定义约束
由于编写自定义约束是本文的关键部分,所以我们将主要关注于尽可能地简化相关操作。下面让我们开始吧。
正如我们在第一部分中看见的,约束主要由两个部分组成:
1、注释(annotation) 2、实现(implementation)
约束注释的定义
每个约束对应一个注释。你可以把注释当作一个安全的别名和一个描述符。我们可以在声明约束注释的时候添加一个以上的参数,用以实现各种不同的自定义行为。
public class Order { @NotNull @OrderNumber private String number; @Range(min=0) private BigDecimal totalPrice; ...}
我们来看一下@OrderNumber注释的定义:
@Target({METHOD, FIELD})@Retention(RUNTIME)@ConstraintValidator(OrderNumberValidator.class)public @interface OrderNumber { String message() default "{error.orderNumber}"; String[] groups() default {}; }
约束注释只比一般的注释多了一点东西:
必须使用运行时拦截策略:Bean验证会在运行时检查你的对象
必须使用@ConstraintValidator注释
必须有message属性
必须有groups属性
@ConstraintValidator表示了这个Bean验证类是一个约束注释。它同样也是实现约束注释的惯例(我们随后会详细介绍它)。
message属性(通常赋予一个默认的关键字)用来覆盖默认的验证错误列表。我们会在以后的文章中详细描述这一部分。
groups属性定义了一些列子约束。它实现了部分验证和按照指定顺序验证。我们会在以后的文章中详细描述这一部分。
在这两个必备属性之外, 我们可以根据具体的验证逻辑来定义一些参数。这些参数会被传递给具体的约束实现。例如,一个@Range(范围)注释可能会需要一个最小值属性和一个最大值属性。
@Target({METHOD, FIELD})@Retention(RUNTIME)@ConstraintValidator(RangeValidator.class)public @interface Range { long max() default Long.MAX_VALUE; long min() default Long.MIN_VALUE; String message() default "{error.range}"; String[] groups() default {}; }
现在,我们能够很快建立一个包含参数的约束了。下面我们需要一些具体的验证逻辑。
约束的实现
约束的实现类与约束的注释接口通过@ConstraintValidator关联。在早先的开发草案中,@ValidatorClass有时用来替代@ConstraintValidator:最终我们改变了这种错误的方式,对不起。约束实现类必须实现一个非常简单的接口Constraint<A extends Annotation>,这里A是一个目标约束注释。
public class OrderNumberValidator implements Constraint { public void initialize(OrderNumber constraintAnnotation) { //no initialization needed } /** * Order number are of the form Nnnn-nnn-nnn when n is a digit * The sum of each nnn numbers must be a multiple of 3 */ public boolean isValid(Object object) { if ( object == null) return true; if ( ! (object instanceof String) ) throw new IllegalArgumentException("@OrderNumber only applies to String"); String orderNumber = (String) object; if ( orderNumber.length() != 12 ) return false; if ( orderNumber.charAt( 0 ) != 'N' || orderNumber.charAt( 4 ) != '-' || orderNumber.charAt( 8 ) != '-' ) return false; try { long result = Integer.parseInt( orderNumber.substring( 1, 4 ) ) + Integer.parseInt( orderNumber.substring( 5, 8 ) ) + Integer.parseInt( orderNumber.substring( 9, 12 ) ); return result % 3 == 0; } catch (NumberFormatException nfe) { return false; } }}
initialize方法通过参数的方式接收约束注释。这个方法一般会执行以下任务:
为isValid方法准备参数
如果需要的话,获得一些扩展资源
正如你所见,这个接口将注意力完全放在了验证逻辑上,将其他所有烦人的部分例如错误显示交给了bean验证提供者。
isValid负责验证具体的值。一些有趣的东西值得我们关注下:
isValid方法必须支持并发调用
当传入的对象类型和预期的不一样,必须抛出错误
null不被认为是无效的:规范文档中推荐将各种验证分离,如果一个值不能为空,则用@NotNull来限制
这些简单的定义让程序员能够自由编写约束进行验证
允许多次使用同种约束
特别需要说明的,在使用groups的时候,你有时可能需要对同一个元素多次应用同一种约束。Bean验证规范特别考虑了这种包含一组约束注释的注释类型。
@Target({METHOD, FIELD})@Retention(RUNTIME)public @interface Patterns { Pattern[] value();}@ConstraintValidator(PatternValidator.class)@Target({METHOD, FIELD})@Retention(RUNTIME)public @interface Pattern { /** regular expression */ String regex(); /** regular expression processing flags */ int flags() default 0; String message() default "{validator.pattern}"; String[] groups() default {};}
在这个例子中,你可能会需要对同一个属性多次使用这种约束验证。
public class Engine { @Patterns( { @Pattern(regex = "^[A-Z0-9-]+___FCKpd___5quot;, message = "must contain alphabetical characters only"), @Pattern(regex = "^....-....-....___FCKpd___5quot;, message="must match ....-....-....") } ) private String serialNumber; ...
构建约束
默认情况下,Bean验证使用无参构造器实例化约束验证实现类。然而,规范也提供了一个扩展,该扩展将指向实例化进程的指针提供给其他依赖的管理工具库,例如Web Beans,Guice,Spring,JBoss Seam甚至JBoss Microcontainer。
根据不同管理工具的功能,我们期望在需要的时候验证的实现类能够接受到注入的资源:这个功能的实现完全取决于所依赖的管理工具。
类级约束
一些人可能会比较担心,能否实现跨越多个属性的验证,或者能否实现直接基于许多属性的约束。最典型的莫过于地址验证。地址通常需要复杂的规则来验证:
街道的名字还稍微能够确定,并且肯定能够进行长度限制
不同国家的邮编构成几乎都不一样
城市通常与邮编互相关联,因此能够实现一些错误检查(假设能够通过一种验证服务来完成)
由于这些互相依赖的条件,一个简单的属性级别的验证很难满足这种需求
Bean验证规范提供的解决方案涵盖了这两个方面:
它能够强制要求一组约束执行后,再执行组(groups)和组员次序(group sequences)内规定的另外一组约束。这个主体将在下一篇博客文章中介绍。
它允许定义类级别的约束
类级别的约束是规定不变的约束(注释和实现),它被应用在class上而不是属性上。换一种说法,类级别的约束接受的是实例化的对象,而不是属性值,即传递给isValid方法的参数。
@Address public class Address { @NotNull @Max(50) private String street1; @Max(50) private String street2; @Max(10) @NotNull private String zipCode; @Max(20) @NotNull String city; @NotNull private Country country; ...}@ConstraintValidator(MultiCountryAddressValidator.class)@Target(TYPE)@Retention(RUNTIME)public @interface Address { String message() default "{error.address}"; String[] groups() default {};}public class MultiCountryAddressValidator implements Constraint { public void initialize(Address constraintAnnotation) { //initialize the zipcode/city/country correlation service } /** * Validate zipcode and city depending on the country */ public boolean isValid(Object object) { if ( ! (object instanceof Address) ) throw new IllegalArgumentException("@Address only applies to Address"); Address address = (Address) object; Country country = address.getCountry(); if ( country.getISO2() == "FR" ) { //check address.getZipCode() structure for France (5 numbers) //check zipcode and city correlation (calling an external service?) return isValid; } else if ( country.getISO2() == "GR" ) { //check address.getZipCode() structure for Greece //no zipcode / city correlation available at the moment return isValid; } ... }}
我们将高级的验证规则写在地址对象的前面,并且通过MultiCountryAddressValidator来实现它。通过处理这个实例化的对象,类级别的验证拥有更大的灵活性,并且能够对相互关联的多个属性进行验证。注意,这里的命令放在了等式的外面(Note that ordering is left out of the equation here),我们会在下一篇文章中回到这个例子。
专家组已经针对各种不同的多属性验证进行了多次讨论:我们认为类级别的约束验证相对其他属性级别的约束验证(包括属性依赖关系验证方式)更加简单和灵活。我们同样欢迎你的任何反馈。
结束语
自定义约束为JSR 303 Bean Validation灵活性表现的核心。编写自定义约束肯定不会被认为是一件困难的事情:
这种验证规则会是你所期望的最严谨验证语法
在代码中小心使用注释名称,将会让你的程序非常易读
可以通过内置约束(例如@NotNull、@Length等)来进行Bean验证,本文介绍的主要部分为基础验证的扩展。我们非常鼓励开发人员针对具体的业务需求编写自定义约束。
编写自定义约束
由于编写自定义约束是本文的关键部分,所以我们将主要关注于尽可能地简化相关操作。下面让我们开始吧。
正如我们在第一部分中看见的,约束主要由两个部分组成:
1、注释(annotation) 2、实现(implementation)
约束注释的定义
每个约束对应一个注释。你可以把注释当作一个安全的别名和一个描述符。我们可以在声明约束注释的时候添加一个以上的参数,用以实现各种不同的自定义行为。
public class Order { @NotNull @OrderNumber private String number; @Range(min=0) private BigDecimal totalPrice; ...}
我们来看一下@OrderNumber注释的定义:
@Target({METHOD, FIELD})@Retention(RUNTIME)@ConstraintValidator(OrderNumberValidator.class)public @interface OrderNumber { String message() default "{error.orderNumber}"; String[] groups() default {}; }
约束注释只比一般的注释多了一点东西:
必须使用运行时拦截策略:Bean验证会在运行时检查你的对象
必须使用@ConstraintValidator注释
必须有message属性
必须有groups属性
@ConstraintValidator表示了这个Bean验证类是一个约束注释。它同样也是实现约束注释的惯例(我们随后会详细介绍它)。
message属性(通常赋予一个默认的关键字)用来覆盖默认的验证错误列表。我们会在以后的文章中详细描述这一部分。
groups属性定义了一些列子约束。它实现了部分验证和按照指定顺序验证。我们会在以后的文章中详细描述这一部分。
在这两个必备属性之外, 我们可以根据具体的验证逻辑来定义一些参数。这些参数会被传递给具体的约束实现。例如,一个@Range(范围)注释可能会需要一个最小值属性和一个最大值属性。
@Target({METHOD, FIELD})@Retention(RUNTIME)@ConstraintValidator(RangeValidator.class)public @interface Range { long max() default Long.MAX_VALUE; long min() default Long.MIN_VALUE; String message() default "{error.range}"; String[] groups() default {}; }
现在,我们能够很快建立一个包含参数的约束了。下面我们需要一些具体的验证逻辑。
约束的实现
约束的实现类与约束的注释接口通过@ConstraintValidator关联。在早先的开发草案中,@ValidatorClass有时用来替代@ConstraintValidator:最终我们改变了这种错误的方式,对不起。约束实现类必须实现一个非常简单的接口Constraint<A extends Annotation>,这里A是一个目标约束注释。
public class OrderNumberValidator implements Constraint { public void initialize(OrderNumber constraintAnnotation) { //no initialization needed } /** * Order number are of the form Nnnn-nnn-nnn when n is a digit * The sum of each nnn numbers must be a multiple of 3 */ public boolean isValid(Object object) { if ( object == null) return true; if ( ! (object instanceof String) ) throw new IllegalArgumentException("@OrderNumber only applies to String"); String orderNumber = (String) object; if ( orderNumber.length() != 12 ) return false; if ( orderNumber.charAt( 0 ) != 'N' || orderNumber.charAt( 4 ) != '-' || orderNumber.charAt( 8 ) != '-' ) return false; try { long result = Integer.parseInt( orderNumber.substring( 1, 4 ) ) + Integer.parseInt( orderNumber.substring( 5, 8 ) ) + Integer.parseInt( orderNumber.substring( 9, 12 ) ); return result % 3 == 0; } catch (NumberFormatException nfe) { return false; } }}
initialize方法通过参数的方式接收约束注释。这个方法一般会执行以下任务:
为isValid方法准备参数
如果需要的话,获得一些扩展资源
正如你所见,这个接口将注意力完全放在了验证逻辑上,将其他所有烦人的部分例如错误显示交给了bean验证提供者。
isValid负责验证具体的值。一些有趣的东西值得我们关注下:
isValid方法必须支持并发调用
当传入的对象类型和预期的不一样,必须抛出错误
null不被认为是无效的:规范文档中推荐将各种验证分离,如果一个值不能为空,则用@NotNull来限制
这些简单的定义让程序员能够自由编写约束进行验证
允许多次使用同种约束
特别需要说明的,在使用groups的时候,你有时可能需要对同一个元素多次应用同一种约束。Bean验证规范特别考虑了这种包含一组约束注释的注释类型。
@Target({METHOD, FIELD})@Retention(RUNTIME)public @interface Patterns { Pattern[] value();}@ConstraintValidator(PatternValidator.class)@Target({METHOD, FIELD})@Retention(RUNTIME)public @interface Pattern { /** regular expression */ String regex(); /** regular expression processing flags */ int flags() default 0; String message() default "{validator.pattern}"; String[] groups() default {};}
在这个例子中,你可能会需要对同一个属性多次使用这种约束验证。
public class Engine { @Patterns( { @Pattern(regex = "^[A-Z0-9-]+___FCKpd___5quot;, message = "must contain alphabetical characters only"), @Pattern(regex = "^....-....-....___FCKpd___5quot;, message="must match ....-....-....") } ) private String serialNumber; ...
构建约束
默认情况下,Bean验证使用无参构造器实例化约束验证实现类。然而,规范也提供了一个扩展,该扩展将指向实例化进程的指针提供给其他依赖的管理工具库,例如Web Beans,Guice,Spring,JBoss Seam甚至JBoss Microcontainer。
根据不同管理工具的功能,我们期望在需要的时候验证的实现类能够接受到注入的资源:这个功能的实现完全取决于所依赖的管理工具。
类级约束
一些人可能会比较担心,能否实现跨越多个属性的验证,或者能否实现直接基于许多属性的约束。最典型的莫过于地址验证。地址通常需要复杂的规则来验证:
街道的名字还稍微能够确定,并且肯定能够进行长度限制
不同国家的邮编构成几乎都不一样
城市通常与邮编互相关联,因此能够实现一些错误检查(假设能够通过一种验证服务来完成)
由于这些互相依赖的条件,一个简单的属性级别的验证很难满足这种需求
Bean验证规范提供的解决方案涵盖了这两个方面:
它能够强制要求一组约束执行后,再执行组(groups)和组员次序(group sequences)内规定的另外一组约束。这个主体将在下一篇博客文章中介绍。
它允许定义类级别的约束
类级别的约束是规定不变的约束(注释和实现),它被应用在class上而不是属性上。换一种说法,类级别的约束接受的是实例化的对象,而不是属性值,即传递给isValid方法的参数。
@Address public class Address { @NotNull @Max(50) private String street1; @Max(50) private String street2; @Max(10) @NotNull private String zipCode; @Max(20) @NotNull String city; @NotNull private Country country; ...}@ConstraintValidator(MultiCountryAddressValidator.class)@Target(TYPE)@Retention(RUNTIME)public @interface Address { String message() default "{error.address}"; String[] groups() default {};}public class MultiCountryAddressValidator implements Constraint { public void initialize(Address constraintAnnotation) { //initialize the zipcode/city/country correlation service } /** * Validate zipcode and city depending on the country */ public boolean isValid(Object object) { if ( ! (object instanceof Address) ) throw new IllegalArgumentException("@Address only applies to Address"); Address address = (Address) object; Country country = address.getCountry(); if ( country.getISO2() == "FR" ) { //check address.getZipCode() structure for France (5 numbers) //check zipcode and city correlation (calling an external service?) return isValid; } else if ( country.getISO2() == "GR" ) { //check address.getZipCode() structure for Greece //no zipcode / city correlation available at the moment return isValid; } ... }}
我们将高级的验证规则写在地址对象的前面,并且通过MultiCountryAddressValidator来实现它。通过处理这个实例化的对象,类级别的验证拥有更大的灵活性,并且能够对相互关联的多个属性进行验证。注意,这里的命令放在了等式的外面(Note that ordering is left out of the equation here),我们会在下一篇文章中回到这个例子。
专家组已经针对各种不同的多属性验证进行了多次讨论:我们认为类级别的约束验证相对其他属性级别的约束验证(包括属性依赖关系验证方式)更加简单和灵活。我们同样欢迎你的任何反馈。
结束语
自定义约束为JSR 303 Bean Validation灵活性表现的核心。编写自定义约束肯定不会被认为是一件困难的事情:
这种验证规则会是你所期望的最严谨验证语法
在代码中小心使用注释名称,将会让你的程序非常易读
发表评论
-
JBoss Seam事件机制 (4):页面动作
2009-06-24 13:47 1676在JBoss Seam事件机制(1 ... -
JBoss Seam的事件机制 (3)
2009-06-24 13:46 1412JBoss Seam如何做到松耦合 ... -
JBoss Seam的事件机制 (2)
2009-06-24 13:45 1292JBoss Seam如何做到松耦合 ... -
JBoss Seam的事件机制 (1) 内置的上下文事件
2009-06-24 13:45 1655在JBoss Seam的事件机制(1)概述中我们提到Seam提 ... -
Seam 2.1中的安全升级 (二)
2009-06-24 13:43 1617Permission Management 尽管 Ident ... -
Seam 2.1中的安全升级 (一)
2009-06-24 13:41 1375Seam安全升级 by Shane Bryzak,翻译:JS ... -
seam2.1权限验证(15.5)@Restrict注解 (三)
2009-06-24 13:39 142715.6.8. 权限验证的模型 ... -
seam2.1权限验证(15.5)@Restrict注解 (二)
2009-06-24 13:37 159715.6.5. 实体安全控制(Securing Entitie ... -
seam2.1权限验证(15.5)@Restrict注解 (一)
2009-06-24 13:36 235115.5. 错误消息安全API内 ... -
seam2.1权限验证(15.3) JAAS
2009-06-24 13:35 184215.3. 验证Seam安全中的验证特性是基于JAAS (Ja ... -
seam2.1权限验证(15.4) jpa-identity-store
2009-06-24 13:34 206415.4. 身份管理身份管理功能提供了一组标准的API接口,用 ... -
seam发送邮件email示例
2009-06-24 13:33 1601以下转自csdn上的一个回答,留着用的时候参考,防止到时找不到 ... -
扩展seam组件
2009-06-24 13:31 1101任何一个框架都必须拥有一个非常重要的功能:可扩展性。JBoss ...
相关推荐
参考Seam Carving for Content-Aware Image Resizing论文实现代码。 对应blog:http://blog.csdn.net/u011630458/article/details/54171081
- **JBoss Seam**:由JBoss提供的Seam实现,提供了一系列功能强大的工具和服务,帮助开发者快速构建复杂的Web应用。 - **本教程**:主要介绍了JBoss Seam的基本概念、核心组件以及通过一系列示例项目来学习Seam的...
#### 三、使用Seam-gen快速启动 - **准备活动**:设置开发环境。 - **建立一个新的Eclipse项目**:通过 Eclipse 创建 SEAM 项目。 - **创建新动作**:添加新的业务逻辑。 - **创建有动作的表单**:实现表单验证和...
2. **组件类型**:详细列举了Seam支持的各种组件类型,如无状态SessionBean、有状态SessionBean、实体Bean、JavaBeans、消息驱动Bean等。 3. **组件名称和作用域**:讲解了如何定义组件的名字以及它们的作用范围。 4...
如果需要自定义验证逻辑,可以在 `components.xml` 文件中创建一个名为 `identity` 的组件,并指定 `authenticate-method` 为自定义验证方法的 EL 表达式,如 `#{authenticator.authenticate}`。 **编写验证方法**...
Seam 结合了 JavaServer Faces (JSF)、Java Persistence API (JPA)、Java Messaging Service (JMS) 和其他 Java EE 技术,提供了丰富的功能集来帮助开发者快速构建高质量的应用程序。 #### 第一章:Seam 入门 本章...
自定义约束注解可以通过实现 `ConstraintValidator` 接口来定义验证逻辑,并通过 `Constraint` 注解来指定约束的应用范围。 ##### 2.2 验证 Bean 约束 验证 Bean 约束的基本方法是使用 `Validator` 实例提供的 `...
本节介绍如何使用 seam-gen 工具快速生成 Seam 项目模板。 **2.1 准备工作** - **安装环境**:确保已安装 Java 和 Maven。 - **下载工具**:下载最新版本的 seam-gen。 **2.2 设置 Eclipse 项目** - **创建项目*...
5. **Seam安全与身份验证**:讨论Seam提供的安全机制,如角色基础的访问控制(RBAC)和会话管理,以及如何实现用户身份验证和授权。 6. **Seam工作流**:解释Seam如何支持业务流程,如状态机和工作流定义,以及如何...
3. **Seam生成器(Seam Generator)**: 提供了一套工具,用于快速生成Seam项目和组件的模板代码,加速开发进程。 4. **测试框架**: Seam 2.0引入了集成测试框架,使得单元测试和集成测试变得更加方便。 5. **国际...
此外,Seam还集成了数据验证,允许在Entity Bean中定义数据约束,这些约束在页面上即可直接应用,类似于Ruby on Rails的验证机制。这得益于Seam的作者Gavin King同时也是Hibernate的创始人,使得数据持久化和验证的...
12. **Hibernate验证器细节**:深入探讨了公共API、快速失败模式、放宽方法验证要求、编程约束定义等高级特性。 13. **注释处理器**:解释了注释处理器的使用前提、功能、选项及问题。 14. **进一步阅读**:为希望...
4. **安全与身份验证**:了解Seam的安全特性,如身份验证、授权和会话管理。 5. **持久化与数据访问**:研究Seam如何通过JPA进行数据存储和检索,以及Seam的缓存和查询优化功能。 6. **国际化与本地化**:学习如何在...
根据提供的信息,我们可以推断出这是一本...通过对本书的学习,开发者可以掌握如何利用 Seam 快速构建高性能的企业级 Web 应用程序。此外,本书还提供了大量实用案例和最佳实践,帮助读者更好地理解和应用 Seam 框架。
对于初学者来说,它是一个宝贵的资源,可以帮助他们快速理解和掌握Seam框架,从而提高开发效率。 通过深入学习这个Seam 2.0学习文档,开发者可以了解到如何利用Seam简化Java EE应用开发,提升应用程序的可维护性和...
- **Seam编辑器的主要特点**:这部分内容概括了Seam编辑器的一些主要特性,如内容辅助、Open On功能和Seam验证等。 #### 8. Seam视图 - **Seam Components视图**:Seam Dev Tools提供了一个专门用于查看和管理Seam...
### JBoss Seam: Simplicity and Power Beyond Java EE #### 一、Seam简介与特点 **JBoss Seam** 是一款强大的开源框架,它在**Java EE** 的基础上提供了更为简单且功能强大的开发方式,旨在简化企业级应用的开发...
2. **Seam 表单处理**:Seam 支持对 JSF 表单的增强处理,包括自动的表单验证和转换,以及更加丰富的 UI 控件支持。 3. **Seam 消息系统**:Seam 引入了一个强大的消息系统,可以用于在不同的组件之间传递消息,...