程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Java自定義注解和運行時靠反射獲取注解

Java自定義注解和運行時靠反射獲取注解

編輯:JAVA綜合教程

Java自定義注解和運行時靠反射獲取注解


java自定義注解

Java注解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。
注解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中。

1、元注解

元注解是指注解的注解。包括 @Retention @Target @Document @Inherited四種。


1.1、@Retention: 定義注解的保留策略

@Retention(RetentionPolicy.SOURCE)//注解僅存在於源碼中,在class字節碼文件中不包含 @Retention(RetentionPolicy.CLASS)// 默認的保留策略,注解會在class字節碼文件中存在,但運行時無法獲得, @Retention(RetentionPolicy.RUNTIME)// 注解會在class字節碼文件中存在,在運行時可以通過反射獲取到   注解類:
  1. @Retention(RetentionPolicy.RUNTIME)//注解會在class字節碼文件中存在,在運行時可以通過反射獲取到 @Target({ElementType.FIELD,ElementType.METHOD})//定義注解的作用目標**作用范圍字段、枚舉的常量/方法 @Documented//說明該注解將被包含在javadoc中 public@interfaceFieldMeta{   /** *是否為序列號 *@return */ booleanid()defaultfalse; /** *字段名稱 *@return */ Stringname()default""; /** *是否可編輯 *@return */ booleaneditable()defaulttrue; /** *是否在列表中顯示 *@return */ booleansummary()defaulttrue; /** *字段描述 *@return */ Stringdescription()default""; /** *排序字段 *@return */ intorder()default0; }

實體類:
  1. publicclassAnno{   @FieldMeta(id=true,name="序列號",order=1) privateintid; @FieldMeta(name="姓名",order=3) privateStringname; @FieldMeta(name="年齡",order=2) privateintage;   @FieldMeta(description="描述",order=4) publicStringdesc(){ return"java反射獲取annotation的測試"; }   publicintgetId(){ returnid; } publicvoidsetId(intid){ this.id=id; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicintgetAge(){ returnage; } publicvoidsetAge(intage){ this.age=age; }   }
    獲取到注解的幫助類:  
    1. publicclassSortableField{   publicSortableField(){}   publicSortableField(FieldMetameta,Fieldfield){ super(); this.meta=meta; this.field=field; this.name=field.getName(); this.type=field.getType(); }     publicSortableField(FieldMetameta,Stringname,Classtype){ super(); this.meta=meta; this.name=name; this.type=type; }     privateFieldMetameta; privateFieldfield; privateStringname; privateClasstype;   publicFieldMetagetMeta(){ returnmeta; } publicvoidsetMeta(FieldMetameta){ this.meta=meta; } publicFieldgetField(){ returnfield; } publicvoidsetField(Fieldfield){ this.field=field; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; }   publicClassgetType(){ returntype; }   publicvoidsetType(Classtype){ this.type=type; }     }
      運行時獲取注解,首先創建一個基類:  
      1. publicclassParent{   privateClassentity;   publicParent(){ init(); }   @SuppressWarnings("unchecked") publicListinit(){ Listlist=newArrayList(); /**getClass().getGenericSuperclass()返回表示此Class所表示的實體(類、接口、基本類型或void) *的直接超類的Type(Class泛型中的類型),然後將其轉換ParameterizedType。。 *getActualTypeArguments()返回表示此類型實際類型參數的Type對象的數組。 *[0]就是這個數組中第一個了。。 *簡而言之就是獲得超類的泛型參數的實際類型。。*/ entity=(Class)((ParameterizedType)this.getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; //FieldMetafiled=entity.getAnnotation(FieldMeta.class);   if(this.entity!=null){   /**返回類中所有字段,包括公共、保護、默認(包)訪問和私有字段,但不包括繼承的字段 *entity.getFields();只返回對象所表示的類或接口的所有可訪問公共字段 *在class中getDeclared**()方法返回的都是所有訪問權限的字段、方法等; *可看API **/ Field[]fields=entity.getDeclaredFields(); // for(Fieldf:fields){ //獲取字段中包含fieldMeta的注解 FieldMetameta=f.getAnnotation(FieldMeta.class); if(meta!=null){ SortableFieldsf=newSortableField(meta,f); list.add(sf); } }   //返回對象所表示的類或接口的所有可訪問公共方法 Method[]methods=entity.getMethods();   for(Methodm:methods){ FieldMetameta=m.getAnnotation(FieldMeta.class); if(meta!=null){ SortableFieldsf=newSortableField(meta,m.getName(),m.getReturnType()); list.add(sf); } } //這種方法是新建FieldSortCom類實現Comparator接口,來重寫compare方法實現排序 //Collections.sort(list,newFieldSortCom()); Collections.sort(list,newComparator(){ @Override publicintcompare(SortableFields1,SortableFields2){ returns1.getMeta().order()-s2.getMeta().order(); //returns1.getName().compareTo(s2.getName());//也可以用compare來比較 }   }); } returnlist;   } } 創建子類繼承基類:  
        1. publicclassChildextendsParent{   }
          測試類:  
          1. publicclassTestAnnotation{   @SuppressWarnings({"unchecked","rawtypes"}) publicstaticvoidmain(String[]args){ Parentc=newChild(); Listlist=c.init();//獲取泛型中類裡面的注解 //輸出結果 for(SortableFieldl:list){ System.out.println("字段名稱:"+l.getName()+"\t字段類型:"+l.getType()+ "\t注解名稱:"+l.getMeta().name()+"\t注解描述:"+l.getMeta().description()); } } }
            轉:   1、Annotation的工作原理:

            JDK5.0中提供了注解的功能,允許開發者定義和使用自己的注解類型。該功能由一個定義注解類型的語法和描述一個注解聲明的語法,讀取注解的API,一個使用注解修飾的class文件和一個注解處理工具組成。

            Annotation並不直接影響代碼的語義,但是他可以被看做是程序的工具或者類庫。它會反過來對正在運行的程序語義有所影響。

            Annotation可以沖源文件、class文件或者在運行時通過反射機制多種方式被讀取。

            2、@Override注解:
            java.lang
            注釋類型 Override
            @Target(value=METHOD)
            @Retention(value=SOURCE)
            public @interface Override

            表示一個方法聲明打算重寫超類中的另一個方法聲明。如果方法利用此注釋類型進行注解但沒有重寫超類方法,則編譯器會生成一條錯誤消息。

            @Override注解表示子類要重寫父類的對應方法。

            Override是一個Marker annotation,用於標識的Annotation,Annotation名稱本身表示了要給工具程序的信息。

            下面是一個使用@Override注解的例子:

            class A {
                private String id;
                A(String id){
                    this.id = id;
                }
                @Override
                public String toString() {
                    return id;
                }
            }
            3、@Deprecated注解:
            java.lang
            注釋類型 Deprecated
            @Documented
            @Retention(value=RUNTIME)
            public @interface Deprecated

            用 @Deprecated 注釋的程序元素,不鼓勵程序員使用這樣的元素,通常是因為它很危險或存在更好的選擇。在使用不被贊成的程序元素或在不被贊成的代碼中執行重寫時,編譯器會發出警告。

            @Deprecated注解表示方法是不被建議使用的。

            Deprecated是一個Marker annotation。

            下面是一個使用@Deprecated注解的例子:

            class A {
                private String id;
                A(String id){
                    this.id = id;
                }
                @Deprecated
                public void execute(){
                    System.out.println(id);
                }
                public static void main(String[] args) {
                    A a = new A("a123");
                    a.execute();
                }
            }
            4、@SuppressWarnings注解:
            java.lang
            注釋類型 SuppressWarnings
            @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
            @Retention(value=SOURCE)
            public @interface SuppressWarnings

            指示應該在注釋元素(以及包含在該注釋元素中的所有程序元素)中取消顯示指定的編譯器警告。注意,在給定元素中取消顯示的警告集是所有包含元素中取消顯示的警告的超集。例如,如果注釋一個類來取消顯示某個警告,同時注釋一個方法來取消顯示另一個警告,那麼將在此方法中同時取消顯示這兩個警告。

            根據風格不同,程序員應該始終在最裡層的嵌套元素上使用此注釋,在那裡使用才有效。如果要在特定的方法中取消顯示某個警告,則應該注釋該方法而不是注釋它的類。

            @SuppressWarnings注解表示抑制警告。

            下面是一個使用@SuppressWarnings注解的例子:

            @SuppressWarnings("unchecked")
            public static void main(String[] args) {
                List list = new ArrayList();
                list.add("abc");
            }
            5、自定義注解:

            使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義注解時,不能繼承其他的注解或接口。

            自定義最簡單的注解:

            public @interface MyAnnotation {
            
            }

            使用自定義注解:

            public class AnnotationTest2 {
            
                @MyAnnotation
                public void execute(){
                    System.out.println("method");
                }
            }
            5.1、添加變量:
            public @interface MyAnnotation {
            
                String value1();
            }

            使用自定義注解:

            public class AnnotationTest2 {
            
                @MyAnnotation(value1="abc")
                public void execute(){
                    System.out.println("method");
                }
            }

            當注解中使用的屬性名為value時,對其賦值時可以不指定屬性的名稱而直接寫上屬性值接口;除了value意外的變量名都需要使用name=value的方式賦值。

            5.2、添加默認值:
            public @interface MyAnnotation {
            
                String value1() default "abc";
            }
            5.3、多變量使用枚舉:
            public @interface MyAnnotation {
            
                String value1() default "abc";
                MyEnum value2() default MyEnum.Sunny;
            }
            enum MyEnum{
                Sunny,Rainy
            }

            使用自定義注解:

            public class AnnotationTest2 {
            
                @MyAnnotation(value1="a", value2=MyEnum.Sunny)
                public void execute(){
                    System.out.println("method");
                }
            }
            5.4、數組變量:
            public @interface MyAnnotation {
            
                String[] value1() default "abc";
            }

            使用自定義注解:

            public class AnnotationTest2 {
            
                @MyAnnotation(value1={"a","b"})
                public void execute(){
                    System.out.println("method");
                }
            }
            6、設置注解的作用范圍:
            @Documented
            @Retention(value=RUNTIME)
            @Target(value=ANNOTATION_TYPE)
            public @interface Retention

            指示注釋類型的注釋要保留多久。如果注釋類型聲明中不存在 Retention 注釋,則保留策略默認為 RetentionPolicy.CLASS。

            只有元注釋類型直接用於注釋時,Target 元注釋才有效。如果元注釋類型用作另一種注釋類型的成員,則無效。

            public enum RetentionPolicy
            extends Enum

            注釋保留策略。此枚舉類型的常量描述保留注釋的不同策略。它們與 Retention 元注釋類型一起使用,以指定保留多長的注釋。

            CLASS
            編譯器將把注釋記錄在類文件中,但在運行時 VM 不需要保留注釋。
            RUNTIME
            編譯器將把注釋記錄在類文件中,在運行時 VM 將保留注釋,因此可以反射性地讀取。
            SOURCE
            編譯器要丟棄的注釋。

            @Retention注解可以在定義注解時為編譯程序提供注解的保留策略。

            屬於CLASS保留策略的注解有@SuppressWarnings,該注解信息不會存儲於.class文件。

            6.1、在自定義注解中的使用例子:
            @Retention(RetentionPolicy.CLASS)
            public @interface MyAnnotation {
            
                String[] value1() default "abc";
            }
            7、使用反射讀取RUNTIME保留策略的Annotation信息的例子:
            java.lang.reflect
                    接口 AnnotatedElement
            所有已知實現類:
                    AccessibleObject, Class, Constructor, Field, Method, Package

            表示目前正在此 VM 中運行的程序的一個已注釋元素。該接口允許反射性地讀取注釋。由此接口中的方法返回的所有注釋都是不可變並且可序列化的。調用者可以修改已賦值數組枚舉成員的訪問器返回的數組;這不會對其他調用者返回的數組產生任何影響。

            如果此接口中的方法返回的注釋(直接或間接地)包含一個已賦值的 Class 成員,該成員引用了一個在此 VM 中不可訪問的類,則試圖通過在返回的注釋上調用相關的類返回的方法來讀取該類,將導致一個 TypeNotPresentException。

            isAnnotationPresent
            boolean isAnnotationPresent(Class annotationClass)

            如果指定類型的注釋存在於此元素上,則返回 true,否則返回 false。此方法主要是為了便於訪問標記注釋而設計的。

            參數:

            annotationClass - 對應於注釋類型的 Class 對象

            返回:

            如果指定注釋類型的注釋存在於此對象上,則返回 true,否則返回 false

            拋出:

            NullPointerException - 如果給定的注釋類為 null

            從以下版本開始:

            1.5

            getAnnotation
             T getAnnotation(Class annotationClass)

            如果存在該元素的指定類型的注釋,則返回這些注釋,否則返回 null。

            參數:

            annotationClass - 對應於注釋類型的 Class 對象

            返回:

            如果該元素的指定注釋類型的注釋存在於此對象上,則返回這些注釋,否則返回 null

            拋出:

            NullPointerException - 如果給定的注釋類為 null

            從以下版本開始:

            1.5

            getAnnotations
            Annotation[] getAnnotations()

            返回此元素上存在的所有注釋。(如果此元素沒有注釋,則返回長度為零的數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。

            返回:

            此元素上存在的所有注釋

            從以下版本開始:

            1.5

            getDeclaredAnnotations
            Annotation[] getDeclaredAnnotations()

            返回直接存在於此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在於此元素上,則返回長度為零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。

            返回:

            直接存在於此元素上的所有注釋

            從以下版本開始:

            1.5


            下面是使用反射讀取RUNTIME保留策略的Annotation信息的例子:

            自定義注解:

            @Retention(RetentionPolicy.RUNTIME)
            public @interface MyAnnotation {
            
                String[] value1() default "abc";
            }

            使用自定義注解:

            public class AnnotationTest2 {
            
                @MyAnnotation(value1={"a","b"})
                @Deprecated
                public void execute(){
                    System.out.println("method");
                }
            }

            讀取注解中的信息:

            public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
                AnnotationTest2 annotationTest2 = new AnnotationTest2();
                //獲取AnnotationTest2的Class實例
                Class c = AnnotationTest2.class;
                //獲取需要處理的方法Method實例
                Method method = c.getMethod("execute", new Class[]{});
                //判斷該方法是否包含MyAnnotation注解
                if(method.isAnnotationPresent(MyAnnotation.class)){
                    //獲取該方法的MyAnnotation注解實例
                    MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
                    //執行該方法
                    method.invoke(annotationTest2, new Object[]{});
                    //獲取myAnnotation
                    String[] value1 = myAnnotation.value1();
                    System.out.println(value1[0]);
                }
                //獲取方法上的所有注解
                Annotation[] annotations = method.getAnnotations();
                for(Annotation annotation : annotations){
                    System.out.println(annotation);
                }
            }
            8、限定注解的使用:

            限定注解使用@Target。

            @Documented
            @Retention(value=RUNTIME)
            @Target(value=ANNOTATION_TYPE)
            public @interface Target

            指示注釋類型所適用的程序元素的種類。如果注釋類型聲明中不存在 Target 元注釋,則聲明的類型可以用在任一程序元素上。如果存在這樣的元注釋,則編譯器強制實施指定的使用限制。 例如,此元注釋指示該聲明類型是其自身,即元注釋類型。它只能用在注釋類型聲明上:

            @Target(ElementType.ANNOTATION_TYPE)
                public @interface MetaAnnotationType {
                    ...
                }

            此元注釋指示該聲明類型只可作為復雜注釋類型聲明中的成員類型使用。它不能直接用於注釋:

            @Target({}) 
                public @interface MemberType {
                    ...
                }

            這是一個編譯時錯誤,它表明一個 ElementType 常量在 Target 注釋中出現了不只一次。例如,以下元注釋是非法的:

            @Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
                public @interface Bogus {
                    ...
                }
            public enum ElementType
            extends Enum

            程序元素類型。此枚舉類型的常量提供了 Java 程序中聲明的元素的簡單分類。

            這些常量與 Target 元注釋類型一起使用,以指定在什麼情況下使用注釋類型是合法的。

            ANNOTATION_TYPE
            注釋類型聲明
            CONSTRUCTOR
            構造方法聲明
            FIELD
            字段聲明(包括枚舉常量)
            LOCAL_VARIABLE
            局部變量聲明
            METHOD
            方法聲明
            PACKAGE
            包聲明
            PARAMETER
            參數聲明
            TYPE
            類、接口(包括注釋類型)或枚舉聲明


            注解的使用限定的例子:

            @Target(ElementType.METHOD)
            public @interface MyAnnotation {
            
                String[] value1() default "abc";
            }
            9、在幫助文檔中加入注解:

            要想在制作JavaDoc文件的同時將注解信息加入到API文件中,可以使用java.lang.annotation.Documented。

            在自定義注解中聲明構建注解文檔:

            @Documented
            public @interface MyAnnotation {
            
                String[] value1() default "abc";
            }
            

            使用自定義注解:

            public class AnnotationTest2 {
            
                @MyAnnotation(value1={"a","b"})
                public void execute(){
                    System.out.println("method");
                }
            }
            10、在注解中使用繼承:

            默認情況下注解並不會被繼承到子類中,可以在自定義注解時加上java.lang.annotation.Inherited注解聲明使用繼承。

            @Documented
            @Retention(value=RUNTIME)
            @Target(value=ANNOTATION_TYPE)
            public @interface Inherited

            指示注釋類型被自動繼承。如果在注釋類型聲明中存在 Inherited 元注釋,並且用戶在某一類聲明中查詢該注釋類型,同時該類聲明中沒有此類型的注釋,則將在該類的超類中自動查詢該注釋類型。此過程會重復進行,直到找到此類型的注釋或到達了該類層次結構的頂層 (Object) 為止。如果沒有超類具有該類型的注釋,則查詢將指示當前類沒有這樣的注釋。

            注意,如果使用注釋類型注釋類以外的任何事物,此元注釋類型都是無效的。還要注意,此元注釋僅促成從超類繼承注釋;對已實現接口的注釋無效。

             

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved