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