摘要:前言注解就是提供了一種元程序中的元素關聯任何信息和著任何元數據的途徑和方法。注解是一個接口,程序可以通過反射來獲取指定程序元素的對象,然后通過對象來獲取注解里面的元數據。注解是及以后版本引入的。綜上所述元數據以標簽的形式存在于代碼中。
前言
Annotation(注解)就是Java提供了一種元程序中的元素關聯任何信息和著任何元數據(metadata)的途徑和方法。Annotion(注解)是一個接口,程序可以通過反射來獲取指定程序元素的Annotion對象,然后通過Annotion對象來獲取注解里面的元數據。Annotation(注解)是JDK5.0及以后版本引入的。它可以用于創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。從某些方面看,annotation就像修飾符一樣被使用,并應用于包、類 型、構造方法、方法、成員變量、參數、本地變量的聲明中。這些信息被存儲在Annotation的“name=value”結構對中。
注解基礎Annotation的成員在Annotation類型中以無參數的方法的形式被聲明。其方法名和返回值定義了該成員的名字和類型。在此有一個特定的默認語法:允許聲明任何Annotation成員的默認值:一個Annotation可以將name=value對作為沒有定義默認值的Annotation成員的值,當然也可以使用name=value對來覆蓋其它成員默認值。這一點有些近似類的繼承特性,父類的構造函數可以作為子類的默認構造函數,但是也可以被子類覆蓋。
Annotation能被用來為某個程序元素(類、方法、成員變量等)關聯任何的信息。需要注意的是,這里存在著一個基本的規則:Annotation不能影響程序代碼的執行,無論增加、刪除 Annotation,代碼都始終如一的執行。另外,盡管一些annotation通過java的反射api方法在運行時被訪問,而java語言解釋器在工作時忽略了這些annotation。正是由于java虛擬機忽略了Annotation,導致了annotation類型在代碼中是“不起作用”的; 只有通過某種配套的工具才會對annotation類型中的信息進行訪問和處理。本文中將涵蓋標準的Annotation和meta-annotation類型,陪伴這些annotation類型的工具是java編譯器(當然要以某種特殊的方式處理它們)。
元數據從metadata一詞譯來,就是“關于數據的數據”的意思。
元數據的功能作用有很多,比如:你可能用過Javadoc的注釋自動生成文檔。這就是元數據功能的一種。總的來說,元數據可以用來創建文檔,跟蹤代碼的依賴性,執行編譯時格式檢查,代替已有的配置文件。如果要對于元數據的作用進行分類,目前還沒有明確的定義,不過我們可以根據它所起的作用,大致可分為三類:
編寫文檔:通過代碼里標識的元數據生成文檔
代碼分析:通過代碼里標識的元數據對代碼進行分析
編譯檢查:通過代碼里標識的元數據讓編譯器能實現基本的編譯檢查
在Java中元數據以標簽的形式存在于Java代碼中,元數據標簽的存在并不影響程序代碼的編譯和執行,它只是被用來生成其它的文件或針在運行時知道被運行代碼的描述信息。
綜上所述:
元數據以標簽的形式存在于Java代碼中。
元數據描述的信息是類型安全的,即元數據內部的字段都是有明確類型的。
元數據需要編譯器之外的工具額外的處理用來生成其它的程序部件。
元數據可以只存在于Java源代碼級別,也可以存在于編譯之后的Class文件內部。
常用注解根據注解參數的個數,我們可以將注解分為三類:
標記注解:一個沒有成員定義的Annotation類型被稱為標記注解。這種Annotation類型僅使用自身的存在與否來為我們提供信息。比如后面的系統注解@Override;
單值注解
完整注解
根據注解使用方法和用途,我們可以將Annotation分為三類:
JDK內置系統注解
元注解
自定義注解
自定義注解在這里就只給個例子,請務必查閱參考文章的第二篇
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitColor { /** * 顏色枚舉 * @author peida * */ public enum Color{ BULE,RED,GREEN}; /** * 顏色屬性 * @return */ Color fruitColor() default Color.GREEN; }
順便再強調幾個重要的點:
注解元素必須有確定的值,要么在定義注解的默認值中指定,要么在使用注解時指定,非基本類型的注解元素的值不可為null。因此, 使用空字符串或0作為默認值是一種常用的做法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,因為每個注解的聲明中,所有元素都存在,并且都具有相應的值,為了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義注解時,這已經成為一個習慣用法。
Annotation類型里面的參數只能用public或默認(default)這兩個訪問權修飾.例如,String value();這里把方法設為defaul默認類型。
參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這里的參數成員就為String。
如果只有一個參數成員,最好把參數名稱設為"value",后加小括號.例:下面的例子FruitName注解就只有一個參數成員。
處理注解Java使用Annotation接口來代表程序元素前面的注解,該接口是所有Annotation類型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口代表程序中可以接受注解的程序元素,該接口主要有如下幾個實現類:
Class:類定義
Constructor:構造器定義
Field:累的成員變量定義
Method:類的方法定義
Package:類的包定義
java.lang.reflect 包下主要包含一些實現反射功能的工具類,實際上,java.lang.reflect 包所有提供的反射API擴充了讀取運行時Annotation信息的能力。當一個Annotation類型被定義為運行時的Annotation后,該注解才能是運行時可見,當class文件被裝載時被保存在class文件中的Annotation才會被虛擬機讀取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過反射獲取了某個類的AnnotatedElement對象之后,程序就可以調用該對象的如下四個個方法來訪問Annotation信息:
方法1:
方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有注解。
方法3:boolean is AnnotationPresent(Class annotationClass):判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false.
方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。
下面給一個處理的例子
@Target(value = { ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) public @interface UserAnnotation { public int id() default 0; public String name() default ""; public int age() default 18; public String gender() default "M"; } public class TestMain { @UserAnnotation(age=20,gender="F",id=2014,name="zhangsan")//注解的使用 private Object obj; public static void main(String[] args) throws Exception { Filed objField = TestMain.class.getField("obj"); UserAnnotation ua = objField.getAnnotation(UserAnnotation.class);//得到注解,起到了標記的作用 System.out.println(ua.age()+","+ua.gender()+","+ua.id()+","+ua.name()); //***進一步操作的話,假設Object要指向一個User類,那么可以講注解的值給他 TestMain tm = new TestMain(); objFiled.set(tm,new User(ua.age(),ua.gender(),ua.id(),ua.name())); //不錯吧,將自己的信息送給obj,起到了附加信息的作用 //-----------請自由遐想吧~~,下面來說說注解怎么能獲得注解自己的注解------------- Target t = ua.annotationType().getAnnotation(Target.class) ElementType[] values = t.value(); //~~~~~~~~~~~~~~完了,再一次自由遐想吧~~~~~~~~~~~~~~ Sysout.out.println("注意:是遐想,不是瞎想!!"); } }注解實現原理
前面介紹了如何使用Java內置的注解以及如何自定義一個注解,接下去看看注解實現的原理,看看在Java的大體系下面是如何對注解的支持的。還是回到上面自定義注解的例子,對于注解Test,如下,如果對AnnotationTest類進行注解,則運行時可以通過AnnotationTest.class.getAnnotation(Test.class)獲取注解聲明的值,從上面的句子就可以看出,它是從class結構中獲取出Test注解的,所以肯定是在某個時候注解被加入到class結構中去了。
@Test("test") public class AnnotationTest { public void test(){ } }
從java源碼到class字節碼是由編譯器完成的,編譯器會對java源碼進行解析并生成class文件,而注解也是在編譯時由編譯器進行處理,編譯器會對注解符號處理并附加到class結構中,根據jvm規范,class文件結構是嚴格有序的格式,唯一可以附加信息到class結構中的方式就是保存到class結構的attributes屬性中。我們知道對于類、字段、方法,在class結構中都有自己特定的表結構,而且各自都有自己的屬性,而對于注解,作用的范圍也可以不同,可以作用在類上,也可以作用在字段或方法上,這時編譯器會對應將注解信息存放到類、字段、方法自己的屬性上。
在我們的AnnotationTest類被編譯后,在對應的AnnotationTest.class文件中會包含一個RuntimeVisibleAnnotations屬性,由于這個注解是作用在類上,所以此屬性被添加到類的屬性集上。即Test注解的鍵值對value=test會被記錄起來。而當JVM加載AnnotationTest.class文件字節碼時,就會將RuntimeVisibleAnnotations屬性值保存到AnnotationTest的Class對象中,于是就可以通過AnnotationTest.class.getAnnotation(Test.class)獲取到Test注解對象,進而再通過Test注解對象獲取到Test里面的屬性值。
這里可能會有疑問,Test注解對象是什么?其實注解被編譯后的本質就是一個繼承Annotation接口的接口,所以@Test其實就是“public interface Test extends Annotation”,當我們通過AnnotationTest.class.getAnnotation(Test.class)調用時,JDK會通過動態代理生成一個實現了Test接口的對象,并把將RuntimeVisibleAnnotations屬性值設置進此對象中,此對象即為Test注解對象,通過它的value()方法就可以獲取到注解值。
Java注解實現機制的整個過程如上面所示,它的實現需要編譯器和JVM一起配合。
參考文章注解基本概念
自定義注解入門
注解的基本原理和編程實現
注解機制及其原理
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67206.html
摘要:本文會以引出問題為主,后面有時間的話,筆者陸續會抽些重要的知識點進行詳細的剖析與解答。敬請關注服務端思維微信公眾號,獲取最新文章。 原文地址:梁桂釗的博客博客地址:http://blog.720ui.com 這里,筆者結合自己過往的面試經驗,整理了一些核心的知識清單,幫助讀者更好地回顧與復習 Java 服務端核心技術。本文會以引出問題為主,后面有時間的話,筆者陸續會抽些重要的知識點進...
摘要:今天我想聊聊的另一個很棒的特性就是它的可擴展性。的擴展機制在的官網上,描述自己是一個高性能的框架。接下來的章節中我們會慢慢揭開擴展機制的神秘面紗。擴展擴展點的實現類。的定義在配置文件中可以看到文件中定義了個的擴展實現。 摘要: 在Dubbo的官網上,Dubbo描述自己是一個高性能的RPC框架。今天我想聊聊Dubbo的另一個很棒的特性, 就是它的可擴展性。 Dubbo的擴展機制 在Dub...
摘要:序本文主要聊聊中的注解。這里從獲取注解有個可以標注使用哪個,這里的就是尋找這個標識。推薦注解指定,然后的返回,讓它去尋找默認的自己應用里頭都默認定義一個給托管 序 本文主要聊聊spring中的async注解。 AsyncConfigurer @EnableAsync(proxyTargetClass = true) @Configuration public class AsyncCo...
閱讀 1768·2021-10-11 10:57
閱讀 2352·2021-10-08 10:14
閱讀 3393·2019-08-29 17:26
閱讀 3340·2019-08-28 17:54
閱讀 3021·2019-08-26 13:38
閱讀 2885·2019-08-26 12:19
閱讀 3608·2019-08-23 18:05
閱讀 1277·2019-08-23 17:04