站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题通过元数据验证对象

通过元数据验证对象

摘要: 通过元数据验证对象
内容: 很多开发人员一直都在抱怨,除了java代码,他们还要管理众多的XML配置文件。有了最近增加到java的元数据,通过使用标注(注解),框架里的普通详细配置信息现在都可以嵌入java文件里了。Sun的文章“J2SE 5.0 概要”(”J2SE 5.0 in a Nutshell.”)对元数据和标注进行了简要介绍。

这篇文章中,我们将总结现今的配置数据是如何管理的,紧接着的一个实现,标注如何在一个简单的验证框架使用,描述了日后元数据将提供什么样的功能。

版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接
作者:Jacob Hookomginge(作者的Blog:http://blog.matrix.org.cn/page/ginge)
原文:http://www.onjava.com/pub/a/onjava/2005/01/19/metadata_validation.html
译文:http://www.matrix.org.cn/resource/article/44/44151_metadata_validation.html
关键字:metadata;validation

使用框架的今天

在我们日常的作业中,我们都使用了框架来处理这些事情,例如持久化,用户输入,验证,web服务以及工作流。为了与这些框架一起工作,我们不得不通过不同的方法在业务对象里做绑定。一些开发人员使用的与框架绑定的方法是:
1,实现或者继承框架提供的类。一个这样的例子就是为了处理用户的输入,Struts里继承ActionForm。为了能够使用该框架,这是强制的,并且需要开发人员编写和维护专门的业务对象(Employee对象和EmployeeForm)。
2,维护单独的配置文件,这些配置文件把Java对象和方法映射到该框架。Hibernate,Struts以及JavaServerFace都大量的使用了XML配置文件。然而,这些对于Java代码很不显眼,我们失去了编译时验证以及不得不在不同的位置维护数据――在XML文件和在不同的Java文件中。

元数据将提供什么?

元数据允许我们绑定框架相关的配置数据到我们的业务对象而无须改变或者继承任何对象固有的职责。我喜欢将元数据跟Javadoc注释做比较。如果你改变了一个Javadoc注释,它并不会改变你的代码的行为的,除非你确实用到Javadoc命令。概念上,元数据以相似的方式运作。你可以把配置数据添加到你的对象,不用改变代码的行为,除非你要寻找该特定的元数据。既然元数据以这样的方式运作,你可以使用标注(Java元数据)来支持持久化,支持你的web应用框架。斟酌这个例子:
@Column('usrEmail')
@ValidateEmail
public void setEmail(String email) {
this.email = email;
}


在上面的例子里,@Column('usrEmail')是一个会在你的持久化框架使用的标注,而@ValidateEmail是在你的web框架里为了验证用户输入使用的。注意到支持这两个框架我们不需要改变setEmail(String)方法,从而保持了它的原汁原味,以及通有的简单性。

最后,如前所述,主要的好处就是配置数据可以在你的java对象里以一种类型安全的方式维护。不需要额外的剪切和粘贴类名,方法名到单独的XML文件里,却同时确保了名称与java代码相一致。以此代替的是仅仅通过标注在你的代码里的方法里声明配置数据。

验证用户输入
一个的使用元数据的优秀例子是在一个简单的框架哩验证用户输入。有了这个框架,结果是允许开发人员去像这样去装饰对象:
@ValidateRequired
@ValidateEmail
public void setEmail(String email) {
this.email = email;
}

@ValidateRequired
@ValidateLength(min=6,max=12)
public void setPassword(String password) {
this.password = password;
}


同时,开发人员应该能够对一个已标注bean的属性验证输入:
Validator.validate(loginBean, "email", "yourname@onjava.com");
Validator.validate(loginBean, "password", ""); // 非法



实现元数据验证
让我们来看可以定义多种验证的情形。这里有一个关于ValidateLength 和 ValidateExpr标注是怎样的例子:
//例子 @ValidateLength(min=6,max=8)
public @interface ValidateLength {
int min() default 0;
int max() default Integer.MAX_VALUE;
}


// 例子 @ValidateExpr("^(\\w){0,2}$");
public @interface ValidateExpr {
String value();
}


当在一个框架里使用标注的时候一些问题出现了。首先,我们除了绑定状态,不能给标注绑定任何的行为或者操作。其次,没有任何方法知道有哪些标注是用于验证的。这是因为标注不允许继承(extends 或者 implements),也就意味着进行标注自省时,没有instanceof能力。那么,我们怎样才能够在我们的框架里插入 验证元数据?仅仅对标注进行标注!
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Validate {
Class<? extends ValidateHandler> value();
}


如你在上面所看到的Validate标注将为运行时反射保留
(@Retention(RetentionPolicy.RUNTIME))同时它也被声明为标注其他的标注
(@Target(ElementType.ANNOTATION_TYPE))。另外,这个将处理验证逻辑的Validate标注仅有一个指定了一个ValidateHandler实例的Class变量。在我们进一步深入之前,让我们来看看Validate是如何应用到我们的ValidateExpr标注的:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Validate(ValidateExprHandler.class)
public @interface ValidateExpr {
String value();
}


有三个标注已添加到我们ValidateExpr标注里了。前两个是明了的,但是第三个,
@Validate(ValidateExprHandler.class)解决了前面提及到的在一个框架里使用标注所碰到的两个问题。我们现在提供了一个方法,就是标记所有Validate标注的验证器,而这些Validate标注可以通过反射找到。同时,我们通过ValidateExprHandler提供了一个支持处理ValidaeExpr标注的Class类型。让我们看看ValidateExprHandler是如何被实现的(在这篇文章底部的Resources包含了这个和其他例子样例源码.zip)

// 如在Validate标注使用的接口
public interface ValidateHandler
{
public void validate(T settings, Object value)
throws ValidationException;

public Class getSettingsType();
}


// 与ValidateExpr标注使用的处理者
public class ValidateExprHandler
implements ValidateHandler
{
public void validate(ValidateExpr settings,
Object value)
throws ValidationException
{
String i = (value != null)
? value.toString()
: "";

if (!Pattern.matches(settings.value(), i))
{
throw new ValidationException(i
+ " does not match the pattern "
+ settings.value());
}
}

public Class getSettingsType()
{
return ValidateExpr.class;
}
}


快速总结一下我们迄今所完成的:
对于每个验证器,我们定义一个标注,实现一个ValidateHandler类以提供标注实际的行为。
既然对于Java元数据没有任何继承机制,我们用一个标记标注(Validate)以使我们的验证框架可以在运行时使用反射找到验证器的实现。
Validatehandler接口允许一个标注委派其行为,这些行为将在处理时被使用。


处理验证器

现在该是时候操作处理我们的验证标注了。在本文前面,我展示了一个验证器工具的例子,也就是关注找寻和处理声明在你的Bean里的验证器。
Validator.validate(loginBean, "email", "yourname@onjava.com");
Validator.validate(loginBean, "password", "");


本质上,目的是使用loginBean,为email (setEmail(String)找到setter方法。在J2SE 5.0,Method实现了AnnotatedElement,这使得我们可以为验证器编写一个一般的标注处理方法:
public static void validate(AnnotatedElement element,
Object value)
throws ValidationException
{
Validate v;
ValidateHandler vh;
Annotation a;

// 抓取所有标注
Annotation[] ma = element.getAnnotations();
for (int i = 0; i < ma.length; i++) {

// 如果一个标注拥有一个验证标注
v = ma[i].annotationType().getAnnotation(Validate.class);
if (v != null) {
try {
// 使用Validate的值创建一个ValidateHandler
vh = v.value().newInstance();

// 使用当前标注作为ValidateHandler的状态
// 会抛出 ValidationException异常
vh.validate(ma[i], value);

} catch (InstantiationException ie) {
} catch (IllegalAccessException iae) {
}



更加详细的描述处理:
1,从元素(setEmail(String))里抓取所有的标注。
2,迭代所有标注检查每个标注是否声明了一个Validate标注
3,如果找到一个Validate标注,则用其值创建一个ValidateHandler的新实例
4,使用从数组里得到的原始标注,将其传递给该ValidateHandler的实例做处理

这就是所有要做的处理。允许你的框架的用户毫不费力的在他们的bean属性上声明类型安全的标注,目的就是以一个不显眼的方式为用户简化处理的细节。确实,重要的是使其他人更加容易的使用你的框架。


路在何方?

像EJB 3.0之类的规范已经增强了元数据对持久化映射的支持了。此外,很多开发人员已经熟悉了XDoclet提供以及使用Javadoc元数据如何对已有框架提供配置细节的了。随着XDclet的流行,使我惊奇的是,在日后发行版本中,框架的开发人员逐渐不再承担提供标注支持了。

就JavaServerFaces而言,John Reynolds最近写了关于验证逻辑改放到何处,以及不赞成当前使用方法的blog。在这篇文章中,为验证框架修改了我们写就的一些代码,你应该结合UIComponent的理念进入到验证处理里。这样就允许了程序员直接在他们的bean里声明验证元数据,而不是分散在JSP页面里。

思索一下,在当今,为了让你的对象在框架里运作需要什么,或者为了迎合你的雇主,你要学习什么API以及规范?XML和Java反射机制是简化我们开发应用和与框架工作方面的一个进步。现在,让我们继续朝着这条路走和以一种认真的看待Java元数据。


相关资源:
Sample source code for the validation utility
"J2SE 5.0 in a Nutshell with an Introduction to Metadata"
J2SE 5.0 Annotations API

关于作者
Jacob Hookom 是一个 McKesson Medical-Surgical的开发人员,一个Sun's JavaServerFaces RI的贡献者,同时也是JavaServerFaces专家组(JavaServerFaces Expert Group)的一个活跃成员。 Java, java, J2SE, j2se, J2EE, j2ee, J2ME, j2me, ejb, ejb3, JBOSS, jboss, spring, hibernate, jdo, struts, webwork, ajax, AJAX, mysql, MySQL, Oracle, Weblogic, Websphere, scjp, scjd 很多开?
↑返回目录
前一篇: 体验 JAVA 5 的新增语言特性
后一篇: Eclipse集成 Lomboz和JBoss开发J2EE