[转载] Java中的元数据
參考鏈接: Java中的數(shù)據(jù)類型
元數(shù)據(jù) 也可能剛聽到元數(shù)據(jù)你會(huì)有點(diǎn)陌生,其實(shí)任何一個(gè)使用過struts,ejb或者h(yuǎn)ibernate的開發(fā)人員都在不知不覺中使用元數(shù)據(jù)。所謂的元數(shù)據(jù)是指用來描述數(shù)據(jù)的數(shù)據(jù),更通俗一點(diǎn)就是描述代碼間關(guān)系,或者代碼與其它資源(例如數(shù)據(jù)庫表)之間內(nèi)在聯(lián)系得數(shù)據(jù),對(duì)Struts來說就是struts-config.xml,對(duì)ejb來說就是ejb-jar.xml和廠商自定義的xml文件,對(duì)hibernate來說就是hbm文件。?
但是現(xiàn)有的所有的以xml或者其它方式存在的元數(shù)據(jù)文件都有以下一些不便之處,第一,與被描述的文件分離,不利于一致性維護(hù)。第二,所有的這些文件都是ascii文件,沒有顯示的類型支持。基于元數(shù)據(jù)的廣泛應(yīng)用JDK1.5引入了Annotation的概念來描述元數(shù)據(jù)。使用過.net的開發(fā)人員一定很熟悉元數(shù)據(jù)的概念,元數(shù)據(jù)的概念在.net中成為Attribute。?
在Java中元數(shù)據(jù)以標(biāo)簽的形式存在于Java代碼中,元數(shù)據(jù)標(biāo)簽的存在并不影響程序代碼的編譯和執(zhí)行,它只是被用來生成其它的文件或針在運(yùn)行時(shí)知道被運(yùn)行代碼的描述信息。?
綜上所述:?
第一, 元數(shù)據(jù)以標(biāo)簽的形式存在于Java代碼中。?
第二, 元數(shù)據(jù)描述的信息是類型安全的,即元數(shù)據(jù)內(nèi)部的字段都是有明確類型的。?
第三, 元數(shù)據(jù)需要編譯器之外的工具額外的處理用來生成其它的程序部件。?
第四, 元數(shù)據(jù)可以只存在于Java源代碼級(jí)別,也可以存在于編譯之后的Class文件內(nèi)部。?
如何創(chuàng)建元數(shù)據(jù)類型 像各種類有可以定義不同的類型一樣,原數(shù)據(jù)也可以定義不同的類型。現(xiàn)在為止,Java語言中已經(jīng)有了四種種的類型:對(duì)象類(class),枚舉(enum),接口(interface)和元數(shù)據(jù)(@interface)。其實(shí)Java中的元數(shù)據(jù)的概念即吸收了.Net中Attribute的概念,有吸收了.net中property的概念。?
Annotation定義
Annotation定義語法為:
modifiers @interface AnnotationName
{
? ?element declaration1
? ?element declaration2
}
?
modifiers指:public,protected,private或者默認(rèn)值(什么也沒有)。 一個(gè)元素的聲明(element declaration)指: type elementName(); 或者 type elementName() default value;?
例如下面代碼定義了一個(gè)Annotation:?
public @interface BugReport
{
? ?String assignedTo() default "[none]";
? ?int severity() = 0;
}
?
而可以通過以下的方式來聲明Annotation:?
AnnotationName(elementName1=value1, elementName2=value2, . . .)
?
元數(shù)聲明的順序沒有關(guān)系,有默認(rèn)值的元素的聲明可以不列在初始化表中,此時(shí)他們使用默認(rèn)值。例如,根據(jù)上述定義如下的三個(gè)Annotation的聲明是等價(jià)的:?
@BugReport(assignedTo="Harry", severity=0)
?
@BugReport(severity=0, assignedTo="Harry")
?
@BugReport(assignedTo="Harry")
?
那些只有一個(gè)元素,且元素名字叫value的Annotation可以使用如下的方式聲明:?
AnnotationName(“somevalue”)?
Annotation中元素的類型必須是下述類型,或者這些類型的組合:?
基本類型 (int, short, long, byte, char, double, float, or boolean)字符創(chuàng)(String)類(Class (可以是形如 Class<? extends MyClass>)的泛型類)枚舉類型(enum)一個(gè)Annotation類型(annotation)上述類型構(gòu)成的數(shù)組
如果Annotation的元素是數(shù)組,則可以做如下聲明: @BugReport(. . ., reportedBy={“Harry”, “Carl”})?
如果數(shù)組中只有一個(gè)元素時(shí)可以做如下聲明:?
@BugReport(. . ., reportedBy=“Joe”) // OK, same as {“Joe”} 如果Annotation元素類型為Annotation時(shí)可以做如下聲明:?
@BugReport(testCase=@TestCase(id=“3352627”), . . .)?
可以對(duì)如下對(duì)象添加Annotation:?
PackagesClasses (including enum)Interfaces (including annotation interfaces)MethodsConstructorsInstance fields (including enum constants)Local variablesParameter variables
標(biāo)準(zhǔn)的Annotation?
JDK1.5提供了若干的標(biāo)準(zhǔn)Annotation來補(bǔ)充語法定義,或者標(biāo)記Annotation。標(biāo)準(zhǔn)的Annotation有以下幾個(gè):?
Annotation使用范圍用途Deprecated所有將目標(biāo)標(biāo)記為不推薦使用SuppressWarnings除了包和Annotation禁止標(biāo)記對(duì)象發(fā)出被標(biāo)記的警告信息Override方法標(biāo)記這個(gè)方法重寫了父類的方法TargetAnnotation標(biāo)記Annotation的適用范圍RetentionAnnotation標(biāo)記Annotation最終駐留的地方DocumentedAnnotation該Annotation在JavaDoc文檔中出現(xiàn)InheritedAnnotation該Annotation默認(rèn)被使用該Annotation的所有子類繼承
?下面具體講解標(biāo)準(zhǔn)Annotation的用法。 常用的Annotation包括以下三個(gè):@Deprecated @SuppressWarnings @Override,他們的用途分別如上表所示。 以下說明的Annotation有一個(gè)共同的特點(diǎn)就是他們都只能用在Annotation的聲明上。 @Target用來標(biāo)記Annotation適用的范圍,@Target有一些預(yù)定義的屬性,如下表所示: 類型|適用范圍 -|-| ANNOTATION_TYPE|只能用在Annotation的聲明中 PACKAGE|用在包上 TYPE|類(包括枚舉)或者接口(包括Annotation) METHOD|方法 CONSTRUCTOR|構(gòu)造方法 FIELD|字段(包含枚舉內(nèi)部的常量) PARAMETER|方法或者構(gòu)造方法的參數(shù) LOCAL_VARIABLE|本地變量?
?@Retention用來標(biāo)記Annotation在那些范圍(源代碼,類文件或者運(yùn)行時(shí))內(nèi)是可用的。@Retention與定義了一些屬性,如下表所示:?
駐留策略描述SOURCEAnnotation只存在于源代碼中,不包括在編譯生成Class文件中CLASSAnnotation存在于源代碼中,也存在于編譯生成的Class文件中,但是在運(yùn)行時(shí)這些Annotation不被JVM裝載。RUNTIMEAnnotation存在于源代碼中,也存在于編譯生成的Class文件中,同時(shí)在運(yùn)行時(shí)這些Annotation被裝載到JVM內(nèi)部,可以使用反射的機(jī)制在運(yùn)行時(shí)使用。
@Documented用來將Annotation顯示在生成的JavaDoc中。 @Inherited只能用來標(biāo)記用在類上的Annotation,用來說明被標(biāo)記的Annotation會(huì)被該類的所有子類自動(dòng)的繼承。?
Annotation應(yīng)用實(shí)例 http://www.onjava.com/pub/a/onjava/2005/01/19/metadata_validation.html by Jacob Hookom?
使用Annotation的一個(gè)例子就是建立一個(gè)簡(jiǎn)單的用戶輸入驗(yàn)證框架,使用這個(gè)框架最終用戶可以以如下的方式來定義字段的校驗(yàn)屬性:?
@ValidateRequired
@ValidateEmail
public void setEmail(String email) {
? ? ? ? this.email = email;
}
?
@ValidateRequired
@ValidateLength(min=6,max=12)
public void setPassword(String password) {
? ? ? ? this.password = password;
}
?
以上的代碼說明,email字段是必須的,且必須滿足email的校驗(yàn)要求,同時(shí)password字段也是必須的,且長度必須在6~12之間。有了這些定義之后我們能夠使用如下的代碼達(dá)到驗(yàn)證的效果:?
Validator.validate(loginBean, "email", "yourname@onjava.com"); //pass
?
Validator.validate(loginBean, "password", ""); // failure
?
要能夠達(dá)到上述的要求,我們必須定義一些Annotation,以下代碼是ValidateLength和ValidateExpr的聲明:?
// Example @ValidateLength(min=6,max=8)
public @interface ValidateLength {
? ? int min() default 0;
? ? int max() default Integer.MAX_VALUE;
}
// Example @ValidateExpr("^(\\w){0,2}$");
public @interface ValidateExpr {
? ? ? ? String value();
}
?
具體開發(fā)的過程中我們會(huì)遇到一些問題,這主要由于兩方面的原意產(chǎn)生第一,Annotation內(nèi)部不能定義方法,只能定義一些狀態(tài)。第二,Annotation不允許使用繼承(extends或者implements),這意味著我們不能在反射的過程中使用instance of這樣的功能。為了識(shí)別出于我們定義的校驗(yàn)相關(guān)的Annotation我們定義了一個(gè)如下的Annotation:?
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Validate {
? ? Class<? extends ValidateHandler> value();
}
?
正如你所看到的Validate可以駐留在JVM內(nèi)部,即它可以在運(yùn)行時(shí)通過反射的方式使用。同時(shí)它必須用來標(biāo)記其他的Annotation,同時(shí)他有一個(gè)Class類型的value元素,這個(gè)類型必須從ValidateHandler繼承而來,主要用來處理具體的驗(yàn)證邏輯。?
在此設(shè)計(jì)之下看看我們?nèi)绾温暶饕粋€(gè)ValidateExpr的Annotation對(duì)象:?
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Validate(ValidateExprHandler.class)
public @interface ValidateExpr {
? ? String value();
}
?
ValidateExpr的前兩個(gè)Annotation不用多講,主要說說@Validate(ValidateExprHandler.class)的含義,這樣解決了我們前邊提到的兩個(gè)問題,第一我們可以看看一個(gè)Annotation是否有Validate類型的Annotation來確定這個(gè)Annotation是不是我們校驗(yàn)框架內(nèi)部使用的Annotation。同時(shí)我們也提供了一個(gè)具體的類ValidateExprHandler來處理校驗(yàn)邏輯。 接下來我們看看ValidateExprHandler的實(shí)現(xiàn):?
// 定義了一個(gè)ValidateHandler接口,
?
// 這個(gè)接口有一個(gè)Annotation類型的模版參數(shù)
?
public interface ValidateHandler<T extends Annotation>
{
? ?public void validate(T settings, Object value)
? ? ? throws ValidationException;
? ?public Class<T> getSettingsType();
}
?
// 一個(gè)ValidateHandler的實(shí)例,用來處理正則表達(dá)式的驗(yàn)證,
?
// 其中的Anotation類型的參數(shù)為ValidateExpr
?
public class ValidateExprHandler implements ValidateHandler<ValidateExpr> {
? ??
? ? 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<ValidateExpr> getSettingsType() {
? ? ? ? return ValidateExpr.class;
? ? }
}
?
說明:?
我們定義了一個(gè)Annotation(Validate)來標(biāo)記我們所有的校驗(yàn)用Annotation,?
同時(shí)每一個(gè)具體的校驗(yàn)用的Annotation(ValidateExpr)都必須提供一個(gè)用來具體處理?
校驗(yàn)邏輯的類(ValidateExprHandler)。?
Annotation不允許繼承,所以我們沒有辦法適用instance of的方法來識(shí)別一個(gè)校驗(yàn)框架?
使用的Annotation,但是通過對(duì)我們使用的校驗(yàn)用的Annotation添加Annotation(Validate)?
我們同樣可以達(dá)到以上的目的。?
ValidateHandler接口允許一個(gè)校驗(yàn)用的Annotation將具體的校驗(yàn)功能已代理的方式讓其它的類來完成。?
我們可以使用如下的方式來處理校驗(yàn)的具體過程:?
說明:在JDK1.5中Method實(shí)現(xiàn)了AnnotatedElement接口,我們可以使用AnnotatedElement來做處理操作?
// 對(duì)一個(gè)方法和將要調(diào)用的參數(shù)值進(jìn)行校驗(yàn)?
public static void validate(AnnotatedElement element, Object value)
? ? ? ? ? ? throws ValidationException
{
? ?Validate v;
? ?ValidateHandler vh;
? ?Annotation a;
? ?// 從該方法上返回所有的Annotation
? ?Annotation[] ma = element.getAnnotations();
? ?for (int i = 0; i < ma.length; i++) {
? ? ? // 如果該Annotation有類型為Validate的Annotation則說明這是我們校驗(yàn)
? ? ? // 框架所使用的Annotation。
? ? ? v = ma[i].annotationType().getAnnotation(Validate.class);
? ? ? if (v != null) {
? ? ? ? ?try {
? ? ? ? ? ? // 使用Annotation中定義的ValidateHandler類來生成ValidateHandler的實(shí)例。
? ? ? ? ? ? vh = v.value().newInstance();
? ? ? ? ? ? // 使用創(chuàng)建的ValidateHandler來做校驗(yàn)操作。
? ? ? ? ? ? // 校驗(yàn)過程中可以拋出ValidationException
? ? ? ? ? ? vh.validate(ma[i], value);
? ? ? ? ?} catch (InstantiationException ie) {
? ? ? ? ?} catch (IllegalAccessException iae) {
? ? ? ? ?}
? ? ? }
? ?}
}
?
轉(zhuǎn)自https://www.cnblogs.com/liuqk/articles/2115778.html
總結(jié)
以上是生活随笔為你收集整理的[转载] Java中的元数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎样呵护友谊_呵护真正的友情,助力漫长的
- 下一篇: [转载] 1.1Java使用JDBC原生