摘要:前言注解就是提供了一種元程序中的元素關(guān)聯(lián)任何信息和著任何元數(shù)據(jù)的途徑和方法。注解是一個接口,程序可以通過反射來獲取指定程序元素的對象,然后通過對象來獲取注解里面的元數(shù)據(jù)。注解是及以后版本引入的。綜上所述元數(shù)據(jù)以標(biāo)簽的形式存在于代碼中。
前言
Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和著任何元數(shù)據(jù)(metadata)的途徑和方法。Annotion(注解)是一個接口,程序可以通過反射來獲取指定程序元素的Annotion對象,然后通過Annotion對象來獲取注解里面的元數(shù)據(jù)。Annotation(注解)是JDK5.0及以后版本引入的。它可以用于創(chuàng)建文檔,跟蹤代碼中的依賴性,甚至執(zhí)行基本編譯時(shí)檢查。從某些方面看,annotation就像修飾符一樣被使用,并應(yīng)用于包、類 型、構(gòu)造方法、方法、成員變量、參數(shù)、本地變量的聲明中。這些信息被存儲在Annotation的“name=value”結(jié)構(gòu)對中。
注解基礎(chǔ)Annotation的成員在Annotation類型中以無參數(shù)的方法的形式被聲明。其方法名和返回值定義了該成員的名字和類型。在此有一個特定的默認(rèn)語法:允許聲明任何Annotation成員的默認(rèn)值:一個Annotation可以將name=value對作為沒有定義默認(rèn)值的Annotation成員的值,當(dāng)然也可以使用name=value對來覆蓋其它成員默認(rèn)值。這一點(diǎn)有些近似類的繼承特性,父類的構(gòu)造函數(shù)可以作為子類的默認(rèn)構(gòu)造函數(shù),但是也可以被子類覆蓋。
Annotation能被用來為某個程序元素(類、方法、成員變量等)關(guān)聯(lián)任何的信息。需要注意的是,這里存在著一個基本的規(guī)則:Annotation不能影響程序代碼的執(zhí)行,無論增加、刪除 Annotation,代碼都始終如一的執(zhí)行。另外,盡管一些annotation通過java的反射api方法在運(yùn)行時(shí)被訪問,而java語言解釋器在工作時(shí)忽略了這些annotation。正是由于java虛擬機(jī)忽略了Annotation,導(dǎo)致了annotation類型在代碼中是“不起作用”的; 只有通過某種配套的工具才會對annotation類型中的信息進(jìn)行訪問和處理。本文中將涵蓋標(biāo)準(zhǔn)的Annotation和meta-annotation類型,陪伴這些annotation類型的工具是java編譯器(當(dāng)然要以某種特殊的方式處理它們)。
元數(shù)據(jù)從metadata一詞譯來,就是“關(guān)于數(shù)據(jù)的數(shù)據(jù)”的意思。
元數(shù)據(jù)的功能作用有很多,比如:你可能用過Javadoc的注釋自動生成文檔。這就是元數(shù)據(jù)功能的一種??偟膩碚f,元數(shù)據(jù)可以用來創(chuàng)建文檔,跟蹤代碼的依賴性,執(zhí)行編譯時(shí)格式檢查,代替已有的配置文件。如果要對于元數(shù)據(jù)的作用進(jìn)行分類,目前還沒有明確的定義,不過我們可以根據(jù)它所起的作用,大致可分為三類:
編寫文檔:通過代碼里標(biāo)識的元數(shù)據(jù)生成文檔
代碼分析:通過代碼里標(biāo)識的元數(shù)據(jù)對代碼進(jìn)行分析
編譯檢查:通過代碼里標(biāo)識的元數(shù)據(jù)讓編譯器能實(shí)現(xiàn)基本的編譯檢查
在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源代碼級別,也可以存在于編譯之后的Class文件內(nèi)部。
常用注解根據(jù)注解參數(shù)的個數(shù),我們可以將注解分為三類:
標(biāo)記注解:一個沒有成員定義的Annotation類型被稱為標(biāo)記注解。這種Annotation類型僅使用自身的存在與否來為我們提供信息。比如后面的系統(tǒng)注解@Override;
單值注解
完整注解
根據(jù)注解使用方法和用途,我們可以將Annotation分為三類:
JDK內(nèi)置系統(tǒng)注解
元注解
自定義注解
自定義注解在這里就只給個例子,請務(wù)必查閱參考文章的第二篇
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitColor { /** * 顏色枚舉 * @author peida * */ public enum Color{ BULE,RED,GREEN}; /** * 顏色屬性 * @return */ Color fruitColor() default Color.GREEN; }
順便再強(qiáng)調(diào)幾個重要的點(diǎn):
注解元素必須有確定的值,要么在定義注解的默認(rèn)值中指定,要么在使用注解時(shí)指定,非基本類型的注解元素的值不可為null。因此, 使用空字符串或0作為默認(rèn)值是一種常用的做法。這個約束使得處理器很難表現(xiàn)一個元素的存在或缺失的狀態(tài),因?yàn)槊總€注解的聲明中,所有元素都存在,并且都具有相應(yīng)的值,為了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負(fù)數(shù),一次表示某個元素不存在,在定義注解時(shí),這已經(jīng)成為一個習(xí)慣用法。
Annotation類型里面的參數(shù)只能用public或默認(rèn)(default)這兩個訪問權(quán)修飾.例如,String value();這里把方法設(shè)為defaul默認(rèn)類型。
參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和 String,Enum,Class,annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.例如,String value();這里的參數(shù)成員就為String。
如果只有一個參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號.例:下面的例子FruitName注解就只有一個參數(shù)成員。
處理注解Java使用Annotation接口來代表程序元素前面的注解,該接口是所有Annotation類型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口代表程序中可以接受注解的程序元素,該接口主要有如下幾個實(shí)現(xiàn)類:
Class:類定義
Constructor:構(gòu)造器定義
Field:累的成員變量定義
Method:類的方法定義
Package:類的包定義
java.lang.reflect 包下主要包含一些實(shí)現(xiàn)反射功能的工具類,實(shí)際上,java.lang.reflect 包所有提供的反射API擴(kuò)充了讀取運(yùn)行時(shí)Annotation信息的能力。當(dāng)一個Annotation類型被定義為運(yùn)行時(shí)的Annotation后,該注解才能是運(yùn)行時(shí)可見,當(dāng)class文件被裝載時(shí)被保存在class文件中的Annotation才會被虛擬機(jī)讀取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過反射獲取了某個類的AnnotatedElement對象之后,程序就可以調(diào)用該對象的如下四個個方法來訪問Annotation信息:
方法1:
方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有注解。
方法3:boolean is AnnotationPresent(Class annotationClass):判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false.
方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會對其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。
下面給一個處理的例子
@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);//得到注解,起到了標(biāo)記的作用 System.out.println(ua.age()+","+ua.gender()+","+ua.id()+","+ua.name()); //***進(jìn)一步操作的話,假設(shè)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("注意:是遐想,不是瞎想!!"); } }注解實(shí)現(xiàn)原理
前面介紹了如何使用Java內(nèi)置的注解以及如何自定義一個注解,接下去看看注解實(shí)現(xiàn)的原理,看看在Java的大體系下面是如何對注解的支持的。還是回到上面自定義注解的例子,對于注解Test,如下,如果對AnnotationTest類進(jìn)行注解,則運(yùn)行時(shí)可以通過AnnotationTest.class.getAnnotation(Test.class)獲取注解聲明的值,從上面的句子就可以看出,它是從class結(jié)構(gòu)中獲取出Test注解的,所以肯定是在某個時(shí)候注解被加入到class結(jié)構(gòu)中去了。
@Test("test") public class AnnotationTest { public void test(){ } }
從java源碼到class字節(jié)碼是由編譯器完成的,編譯器會對java源碼進(jìn)行解析并生成class文件,而注解也是在編譯時(shí)由編譯器進(jìn)行處理,編譯器會對注解符號處理并附加到class結(jié)構(gòu)中,根據(jù)jvm規(guī)范,class文件結(jié)構(gòu)是嚴(yán)格有序的格式,唯一可以附加信息到class結(jié)構(gòu)中的方式就是保存到class結(jié)構(gòu)的attributes屬性中。我們知道對于類、字段、方法,在class結(jié)構(gòu)中都有自己特定的表結(jié)構(gòu),而且各自都有自己的屬性,而對于注解,作用的范圍也可以不同,可以作用在類上,也可以作用在字段或方法上,這時(shí)編譯器會對應(yīng)將注解信息存放到類、字段、方法自己的屬性上。
在我們的AnnotationTest類被編譯后,在對應(yīng)的AnnotationTest.class文件中會包含一個RuntimeVisibleAnnotations屬性,由于這個注解是作用在類上,所以此屬性被添加到類的屬性集上。即Test注解的鍵值對value=test會被記錄起來。而當(dāng)JVM加載AnnotationTest.class文件字節(jié)碼時(shí),就會將RuntimeVisibleAnnotations屬性值保存到AnnotationTest的Class對象中,于是就可以通過AnnotationTest.class.getAnnotation(Test.class)獲取到Test注解對象,進(jìn)而再通過Test注解對象獲取到Test里面的屬性值。
這里可能會有疑問,Test注解對象是什么?其實(shí)注解被編譯后的本質(zhì)就是一個繼承Annotation接口的接口,所以@Test其實(shí)就是“public interface Test extends Annotation”,當(dāng)我們通過AnnotationTest.class.getAnnotation(Test.class)調(diào)用時(shí),JDK會通過動態(tài)代理生成一個實(shí)現(xiàn)了Test接口的對象,并把將RuntimeVisibleAnnotations屬性值設(shè)置進(jìn)此對象中,此對象即為Test注解對象,通過它的value()方法就可以獲取到注解值。
Java注解實(shí)現(xiàn)機(jī)制的整個過程如上面所示,它的實(shí)現(xiàn)需要編譯器和JVM一起配合。
參考文章注解基本概念
自定義注解入門
注解的基本原理和編程實(shí)現(xiàn)
注解機(jī)制及其原理
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/67206.html
摘要:由于類型擦除機(jī)制的存在,泛型類中的類型參數(shù)等信息,在運(yùn)行時(shí)刻是不存在的。對此,對類文件的格式做了修訂,添加了屬性,用來包含不在類型系統(tǒng)中的類型信息。在運(yùn)行時(shí)刻,會讀取屬性的內(nèi)容并提供給反射來使用。 OOP 對象的創(chuàng)建和拷貝 對象的初始化 多態(tài)的實(shí)現(xiàn) 內(nèi)部類、匿名類、靜態(tài)類 對象內(nèi)存模型 上面內(nèi)容均請參考以下文章: 談?wù)凧ava的面向?qū)ο?運(yùn)行時(shí) 異常 聊聊Java的異常機(jī)制及實(shí)現(xiàn) ...
摘要:本文會以引出問題為主,后面有時(shí)間的話,筆者陸續(xù)會抽些重要的知識點(diǎn)進(jìn)行詳細(xì)的剖析與解答。敬請關(guān)注服務(wù)端思維微信公眾號,獲取最新文章。 原文地址:梁桂釗的博客博客地址:http://blog.720ui.com 這里,筆者結(jié)合自己過往的面試經(jīng)驗(yàn),整理了一些核心的知識清單,幫助讀者更好地回顧與復(fù)習(xí) Java 服務(wù)端核心技術(shù)。本文會以引出問題為主,后面有時(shí)間的話,筆者陸續(xù)會抽些重要的知識點(diǎn)進(jìn)...
摘要:今天我想聊聊的另一個很棒的特性就是它的可擴(kuò)展性。的擴(kuò)展機(jī)制在的官網(wǎng)上,描述自己是一個高性能的框架。接下來的章節(jié)中我們會慢慢揭開擴(kuò)展機(jī)制的神秘面紗。擴(kuò)展擴(kuò)展點(diǎn)的實(shí)現(xiàn)類。的定義在配置文件中可以看到文件中定義了個的擴(kuò)展實(shí)現(xiàn)。 摘要: 在Dubbo的官網(wǎng)上,Dubbo描述自己是一個高性能的RPC框架。今天我想聊聊Dubbo的另一個很棒的特性, 就是它的可擴(kuò)展性。 Dubbo的擴(kuò)展機(jī)制 在Dub...
摘要:序本文主要聊聊中的注解。這里從獲取注解有個可以標(biāo)注使用哪個,這里的就是尋找這個標(biāo)識。推薦注解指定,然后的返回,讓它去尋找默認(rèn)的自己應(yīng)用里頭都默認(rèn)定義一個給托管 序 本文主要聊聊spring中的async注解。 AsyncConfigurer @EnableAsync(proxyTargetClass = true) @Configuration public class AsyncCo...
摘要:探索專為而設(shè)計(jì)的將探討進(jìn)行了何種改進(jìn),以及這些改進(jìn)背后的原因。關(guān)于最友好的文章進(jìn)階前言之前就寫過一篇關(guān)于最友好的文章反響很不錯,由于那篇文章的定位就是簡單友好,因此盡可能的摒棄復(fù)雜的概念,只抓住關(guān)鍵的東西來講,以保證大家都能看懂。 周月切換日歷 一個可以進(jìn)行周月切換的日歷,左右滑動的切換月份,上下滑動可以進(jìn)行周,月不同的視圖切換,可以進(jìn)行事件的標(biāo)記,以及節(jié)假日的顯示,功能豐富 Andr...
閱讀 1832·2021-10-11 10:57
閱讀 2453·2021-10-08 10:14
閱讀 3460·2019-08-29 17:26
閱讀 3469·2019-08-28 17:54
閱讀 3080·2019-08-26 13:38
閱讀 3001·2019-08-26 12:19
閱讀 3673·2019-08-23 18:05
閱讀 1360·2019-08-23 17:04