這篇文章主要講解了“如何理解Java注解和注解解析器”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何理解Java注解和注解解析器”吧!
站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到寧江網(wǎng)站設(shè)計(jì)與寧江網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、主機(jī)域名、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋寧江地區(qū)。
元數(shù)據(jù)由metadata譯來(lái),所謂的元數(shù)據(jù)就是“關(guān)于數(shù)據(jù)的數(shù)據(jù)”,更通俗的說(shuō)就是描述數(shù)據(jù)的數(shù)據(jù),對(duì)數(shù)據(jù)及信息資源的描述性信息.比如說(shuō)一個(gè)文本文件,有創(chuàng)建時(shí)間,創(chuàng)建人,文件大小等數(shù)據(jù),這都可以理解為是元數(shù)據(jù).
在java中,元數(shù)據(jù)以標(biāo)簽的形式存在java代碼中,它的存在并不影響程序代碼的編譯和執(zhí)行,通常它被用來(lái)生成其它的文件或運(yùn)行時(shí)知道被運(yùn)行代碼的描述信息。java當(dāng)中的javadoc和注解都屬于元數(shù)據(jù).
注解是從java 5.0開(kāi)始加入,可以用于標(biāo)注包,類,方法,變量等.比如我們常見(jiàn)的@Override,再或者Android源碼中的@hide,@systemApi,@privateApi等
對(duì)于@Override,多數(shù)人往往都是知其然而不知其所以然,今天我就來(lái)聊聊Annotation背后的秘密,開(kāi)始正文.
元注解就是定義注解的注解,是java提供給我們用于定義注解的基本注解.在java.lang.annotation包中我們可以看到目前元注解共有以下幾個(gè):
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
@Retention
@Target
@Inherited
@Documented
@interface
下面我們將集合@Override注解來(lái)解釋著5個(gè)基本注解的用法.
@interface是java中用于聲明注解類的關(guān)鍵字.使用該注解表示將自動(dòng)繼承java.lang.annotation.Annotation類,該過(guò)程交給編譯器完成.
因此我們想要定義一個(gè)注解只需要如下做即可,以@Override注解為例
public @interface Override { }
@Retention:該注解用于定義注解保留策略,即定義的注解類在什么時(shí)候存在(源碼階段 or 編譯后 or 運(yùn)行階段).該注解接受以下幾個(gè)參數(shù): RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME ,其具體使用及含義如下:
來(lái)看一下@Override注解的保留策略:
@Retention(RetentionPolicy.SOURCE) public @interface Override { }
這表明@Override注解只在源碼階段存在,javac在編譯過(guò)程中去去掉該注解.
該注解用于定義注解的作用目標(biāo),即注解可以用在什么地方,比如是用于方法上還是用于字段上,該注解接受以下參數(shù):
以@Override為例,不難看出其作用目標(biāo)為方法:
@Target(ElementType.METHOD) public @interface Override { }
到現(xiàn)在,通過(guò)@interface,@Retention,@Target已經(jīng)可以完整的定義一個(gè)注解,來(lái)看@Override完整定義:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
默認(rèn)情況下,我們自定義的注解用在父類上不會(huì)被子類所繼承.如果想讓子類也繼承父類的注解,即注解在子類也生效,需要在自定義注解時(shí)設(shè)置@Inherited.一般情況下該注解用的比較少.
該注解用于描述其它類型的annotation應(yīng)該被javadoc文檔化,出現(xiàn)在api doc中.
比如使用該注解的@Target會(huì)出出現(xiàn)在api說(shuō)明中.
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
借助@Interface,@Target,@Retention,@Inherited,@Documented這五個(gè)元注解,
我們就可以自定義注解了,其中前三個(gè)注解是任何一個(gè)注解都必備具備的.
格式:
public @interface 注解名 {定義體}
定義體就是方法的集合,每個(gè)方法實(shí)則是聲明了一個(gè)配置參數(shù).方法的名稱作為配置參數(shù)的名稱,方法的返回值類型就是配置參數(shù)的類型.和普通的方法不一樣,可以通過(guò)default關(guān)鍵字來(lái)聲明配置參數(shù)的默認(rèn)值.
需要注意:
此處只能使用public或者默認(rèn)的defalt兩個(gè)權(quán)限修飾符
配置參數(shù)的類型只能使用基本類型(byte,boolean,char,short,int,long,float,double)和String,Enum,Class,annotation
對(duì)于只含有一個(gè)配置參數(shù)的注解,參數(shù)名建議設(shè)置中value,即方法名為value
配置參數(shù)一旦設(shè)置,其參數(shù)值必須有確定的值,要不在使用注解的時(shí)候指定,要不在定義注解的時(shí)候使用default為其設(shè)置默認(rèn)值,對(duì)于非基本類型的參數(shù)值來(lái)說(shuō),其不能為null.
像@Override這樣,沒(méi)有成員定義的注解稱之為標(biāo)記注解.
上面我們已經(jīng)學(xué)會(huì)了如何定義注解,要想注解發(fā)揮實(shí)際作用,需要我們?yōu)樽⒔饩帉懴鄳?yīng)的注解處理器.根據(jù)注解的特性,注解處理器可以分為運(yùn)行時(shí)注解處理和編譯時(shí)注解處理器.運(yùn)行時(shí)處理器需要借助反射機(jī)制實(shí)現(xiàn),而編譯時(shí)處理器則需要借助APT來(lái)實(shí)現(xiàn).
無(wú)論是運(yùn)行時(shí)注解處理器還是編譯時(shí)注解處理器,主要工作都是讀取注解及處理特定注解,從這個(gè)角度來(lái)看注解處理器還是非常容易理解的.
注解處理器是(Annotation Processor)是javac的一個(gè)工具,用來(lái)在編譯時(shí)掃描和編譯和處理注解(Annotation)。你可以自己定義注解和注解處理器去搞一些事情。一個(gè)注解處理器它以Java代碼或者(編譯過(guò)的字節(jié)碼)作為輸入,生成文件(通常是java文件)。這些生成的java文件不能修改,并且會(huì)同其手動(dòng)編寫的java代碼一樣會(huì)被javac編譯。看到這里加上之前理解,應(yīng)該明白大概的過(guò)程了,就是把標(biāo)記了注解的類,變量等作為輸入內(nèi)容,經(jīng)過(guò)注解處理器處理,生成想要生成的java代碼。
熟悉java反射機(jī)制的同學(xué)一定對(duì)java.lang.reflect包非常熟悉,該包中的所有api都支持讀取運(yùn)行時(shí)Annotation的能力,即屬性為@Retention(RetentionPolicy.RUNTIME)的注解.
在java.lang.reflect中的AnnotatedElement接口是所有程序元素的(Class,Method)父接口,我們可以通過(guò)反射獲取到某個(gè)類的AnnotatedElement對(duì)象,進(jìn)而可以通過(guò)該對(duì)象提供的方法訪問(wèn)Annotation信息,常用的方法如下:
運(yùn)行時(shí)注解處理器的編寫本質(zhì)上就是通過(guò)反射獲取注解信息,隨后進(jìn)行其他操作。編譯一個(gè)運(yùn)行時(shí)注解處理器就是這么簡(jiǎn)單。運(yùn)行時(shí)注解通常多用于參數(shù)配置類模塊。
編譯時(shí)注解處理器
不同于運(yùn)行時(shí)注解處理器,編寫編譯時(shí)注解處理器(Annotation Processor Tool).
APT用于在編譯時(shí)期掃描和處理注解信息.一個(gè)特定的注解處理器可以以java源碼文件或編譯后的class文件作為輸入,然后輸出另一些文件,可以是.java文件,也可以是.class文件,但通常我們輸出的是.java文件.(注意:并不是對(duì)源文件修改).如果輸出的是.java文件,這些.java文件回合其他源碼文件一起被javac編譯.
你可能很納悶,注解處理器是到底是在什么階段介入的呢?好吧,其實(shí)是在javac開(kāi)始編譯之前,這也就是通常我們?yōu)槭裁丛敢廨敵?java文件的原因.
注解最早是在java 5引入,主要包含apt和com.sum.mirror包中相關(guān)mirror api,此時(shí)apt和javac是各自獨(dú)立的。從java 6開(kāi)始,注解處理器正式標(biāo)準(zhǔn)化,apt工具也被直接集成在javac當(dāng)中。
我們還是回到如何編寫編譯時(shí)注解處理器這個(gè)話題上,編譯一個(gè)編譯時(shí)注解處理主要分兩步:
1、繼承AbstractProcessor,實(shí)現(xiàn)自己的注解處理器
2、注冊(cè)處理器,并打成jar包
首先來(lái)看一下一個(gè)標(biāo)準(zhǔn)的注解處理器的格式:
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
return super.getSupportedAnnotationTypes();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
編寫一個(gè)注解處理器首先要對(duì)ProcessingEnvironment和RoundEnvironment非常熟悉。接下來(lái)我們一覽這兩個(gè)類的風(fēng)采.首先來(lái)看一下ProcessingEnvironment類:
public interface ProcessingEnvironment { Map<String,String> getOptions(); //Messager用來(lái)報(bào)告錯(cuò)誤,警告和其他提示信息 Messager getMessager(); //Filter用來(lái)創(chuàng)建新的源文件,class文件以及輔助文件 Filer getFiler(); //Elements中包含用于操作Element的工具方法 Elements getElementUtils(); //Types中包含用于操作TypeMirror的工具方法 Types getTypeUtils(); SourceVersion getSourceVersion(); Locale getLocale(); }
Element
element表示一個(gè)靜態(tài)的,語(yǔ)言級(jí)別的構(gòu)件。而任何一個(gè)結(jié)構(gòu)化文檔都可以看作是由不同的element組成的結(jié)構(gòu)體,比如XML,JSON等。
對(duì)于java源文件來(lái)說(shuō), Element代表程序元素:包,類,方法都是一種程序元素 ,他同樣是一種結(jié)構(gòu)化文檔:
package com.closedevice; //PackageElement public class Main{ //TypeElement private int x; //VariableElement private Main(){ //ExecuteableElement } private void print(String msg){ //其中的參數(shù)部分String msg為TypeElement } }
TypeMirror
這三個(gè)類也需要我們重點(diǎn)掌握:
DeclaredType代表聲明類型:類類型還是接口類型,當(dāng)然也包括參數(shù)化類型,比如Set<String>,也包括原始類型
TypeElement代表類或接口元素,而DeclaredType代表類類型或接口類型。
TypeMirror代表java語(yǔ)言中的類型.Types包括基本類型,聲明類型(類類型和接口類型),數(shù)組,類型變量和空類型。也代表通配類型參數(shù),可執(zhí)行文件的簽名和返回類型等。TypeMirror類中最重要的是getKind()方法,該方法返回TypeKind類型,為了方便大家理解,這里附上其源碼:
public enum TypeKind { BOOLEAN,BYTE,SHORT,INT,LONG,CHAR,FLOAT,DOUBLE,VOID,NONE,NULL,ARRAY,DECLARED,ERROR, TYPEVAR,WILDCARD,PACKAGE,EXECUTABLE,OTHER,UNION,INTERSECTION; public boolean isPrimitive() { switch(this) { case BOOLEAN: case BYTE: case SHORT: case INT: case LONG: case CHAR: case FLOAT: case DOUBLE: return true; default: return false; } } }
簡(jiǎn)單來(lái)說(shuō),Element代表源代碼,TypeElement代表的是源碼中的類型元素,比如類。雖然我們可以從TypeElement中獲取類名,TypeElement中不包含類本身的信息,比如它的父類,要想獲取這信息需要借助TypeMirror,可以通過(guò)Element中的asType()獲取元素對(duì)應(yīng)的TypeMirror。
然后來(lái)看一下RoundEnvironment,這個(gè)類比較簡(jiǎn)單,一筆帶過(guò):
public interface RoundEnvironment { boolean processingOver(); //上一輪注解處理器是否產(chǎn)生錯(cuò)誤 boolean errorRaised(); //返回上一輪注解處理器生成的根元素 Set<? extends Element> getRootElements(); //返回包含指定注解類型的元素的集合 Set<? extends Element> getElementsAnnotatedWith(TypeElement a); //返回包含指定注解類型的元素的集合 Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a); }
Filer
Filer用于注解處理器中創(chuàng)建新文件,由于Filer用起來(lái)實(shí)在比較麻煩,后面我們會(huì)使用javapoet簡(jiǎn)化我們的操作.
打包注解處理器的時(shí)候需要一個(gè)特殊的文件 javax.annotation.processing.Processor 在 META-INF/services 路徑下
新建項(xiàng)目必要配置:
//javapoet代碼生成框架 implementation 'com.squareup:javapoet:1.8.0' //注解處理器 implementation 'com.google.auto.service:auto-service:1.0-rc6' annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
編譯時(shí)注解demo示例地址:https://gitee.com/yutg/apt.git
項(xiàng)目結(jié)構(gòu)
--apt-demo ----bindview-annotation(Java Library)//注解定義 ----bindview-api(Android Library)//定義SDK接口方法 ----bindview-compiler(Java Library)//注解處理器相關(guān)操作及生成java文件 ----app(Android App)
感謝各位的閱讀,以上就是“如何理解Java注解和注解解析器”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)如何理解Java注解和注解解析器這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
標(biāo)題名稱:如何理解Java注解和注解解析器
新聞來(lái)源:http://m.newbst.com/article12/jesidc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、關(guān)鍵詞優(yōu)化、網(wǎng)站維護(hù)、標(biāo)簽優(yōu)化、網(wǎng)站設(shè)計(jì)、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)