中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解Java注解和注解解析器

發布時間:2021-10-15 10:18:47 來源:億速云 閱讀:134 作者:iii 欄目:web開發

這篇文章主要講解了“如何理解Java注解和注解解析器”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何理解Java注解和注解解析器”吧!

如何理解Java注解和注解解析器

什么是元數據(metadata)

元數據由metadata譯來,所謂的元數據就是“關于數據的數據”,更通俗的說就是描述數據的數據,對數據及信息資源的描述性信息.比如說一個文本文件,有創建時間,創建人,文件大小等數據,這都可以理解為是元數據.

在java中,元數據以標簽的形式存在java代碼中,它的存在并不影響程序代碼的編譯和執行,通常它被用來生成其它的文件或運行時知道被運行代碼的描述信息。java當中的javadoc和注解都屬于元數據.

什么是注解(Annotation)?

注解是從java 5.0開始加入,可以用于標注包,類,方法,變量等.比如我們常見的@Override,再或者Android源碼中的@hide,@systemApi,@privateApi等

對于@Override,多數人往往都是知其然而不知其所以然,今天我就來聊聊Annotation背后的秘密,開始正文.

元注解就是定義注解的注解,是java提供給我們用于定義注解的基本注解.在java.lang.annotation包中我們可以看到目前元注解共有以下幾個:

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術社區

  2. @Retention

  3. @Target

  4. @Inherited

  5. @Documented

  6. @interface

下面我們將集合@Override注解來解釋著5個基本注解的用法.

@interface

@interface是java中用于聲明注解類的關鍵字.使用該注解表示將自動繼承java.lang.annotation.Annotation類,該過程交給編譯器完成.

因此我們想要定義一個注解只需要如下做即可,以@Override注解為例

public @interface Override { }
需要注意:在定義注解時,不能繼承其他注解或接口
@Retention

@Retention:該注解用于定義注解保留策略,即定義的注解類在什么時候存在(源碼階段 or 編譯后 or 運行階段).該注解接受以下幾個參數: RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME ,其具體使用及含義如下:

如何理解Java注解和注解解析器

來看一下@Override注解的保留策略:

@Retention(RetentionPolicy.SOURCE) public @interface Override { }

這表明@Override注解只在源碼階段存在,javac在編譯過程中去去掉該注解.

@Target

該注解用于定義注解的作用目標,即注解可以用在什么地方,比如是用于方法上還是用于字段上,該注解接受以下參數:

如何理解Java注解和注解解析器

以@Override為例,不難看出其作用目標為方法:

@Target(ElementType.METHOD) public @interface Override { }

到現在,通過@interface,@Retention,@Target已經可以完整的定義一個注解,來看@Override完整定義:

@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
@Inherited

默認情況下,我們自定義的注解用在父類上不會被子類所繼承.如果想讓子類也繼承父類的注解,即注解在子類也生效,需要在自定義注解時設置@Inherited.一般情況下該注解用的比較少.

@Documented

該注解用于描述其它類型的annotation應該被javadoc文檔化,出現在api doc中.

比如使用該注解的@Target會出出現在api說明中.

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target {         ElementType[] value(); }

借助@Interface,@Target,@Retention,@Inherited,@Documented這五個元注解,
我們就可以自定義注解了,其中前三個注解是任何一個注解都必備具備的.

自定義注解

格式:

public @interface 注解名 {定義體}

定義體就是方法的集合,每個方法實則是聲明了一個配置參數.方法的名稱作為配置參數的名稱,方法的返回值類型就是配置參數的類型.和普通的方法不一樣,可以通過default關鍵字來聲明配置參數的默認值.

需要注意:

此處只能使用public或者默認的defalt兩個權限修飾符

配置參數的類型只能使用基本類型(byte,boolean,char,short,int,long,float,double)和String,Enum,Class,annotation

對于只含有一個配置參數的注解,參數名建議設置中value,即方法名為value

配置參數一旦設置,其參數值必須有確定的值,要不在使用注解的時候指定,要不在定義注解的時候使用default為其設置默認值,對于非基本類型的參數值來說,其不能為null.

像@Override這樣,沒有成員定義的注解稱之為標記注解.

注解處理器

上面我們已經學會了如何定義注解,要想注解發揮實際作用,需要我們為注解編寫相應的注解處理器.根據注解的特性,注解處理器可以分為運行時注解處理和編譯時注解處理器.運行時處理器需要借助反射機制實現,而編譯時處理器則需要借助APT來實現.

無論是運行時注解處理器還是編譯時注解處理器,主要工作都是讀取注解及處理特定注解,從這個角度來看注解處理器還是非常容易理解的.

注解處理器是(Annotation Processor)是javac的一個工具,用來在編譯時掃描和編譯和處理注解(Annotation)。你可以自己定義注解和注解處理器去搞一些事情。一個注解處理器它以Java代碼或者(編譯過的字節碼)作為輸入,生成文件(通常是java文件)。這些生成的java文件不能修改,并且會同其手動編寫的java代碼一樣會被javac編譯。看到這里加上之前理解,應該明白大概的過程了,就是把標記了注解的類,變量等作為輸入內容,經過注解處理器處理,生成想要生成的java代碼。

運行時注解處理器(不建議使用)

熟悉java反射機制的同學一定對java.lang.reflect包非常熟悉,該包中的所有api都支持讀取運行時Annotation的能力,即屬性為@Retention(RetentionPolicy.RUNTIME)的注解.

在java.lang.reflect中的AnnotatedElement接口是所有程序元素的(Class,Method)父接口,我們可以通過反射獲取到某個類的AnnotatedElement對象,進而可以通過該對象提供的方法訪問Annotation信息,常用的方法如下:

如何理解Java注解和注解解析器

運行時注解處理器的編寫本質上就是通過反射獲取注解信息,隨后進行其他操作。編譯一個運行時注解處理器就是這么簡單。運行時注解通常多用于參數配置類模塊。

編譯時注解處理器

不同于運行時注解處理器,編寫編譯時注解處理器(Annotation Processor Tool).

APT用于在編譯時期掃描和處理注解信息.一個特定的注解處理器可以以java源碼文件或編譯后的class文件作為輸入,然后輸出另一些文件,可以是.java文件,也可以是.class文件,但通常我們輸出的是.java文件.(注意:并不是對源文件修改).如果輸出的是.java文件,這些.java文件回合其他源碼文件一起被javac編譯.

你可能很納悶,注解處理器是到底是在什么階段介入的呢?好吧,其實是在javac開始編譯之前,這也就是通常我們為什么愿意輸出.java文件的原因.

注解最早是在java 5引入,主要包含apt和com.sum.mirror包中相關mirror api,此時apt和javac是各自獨立的。從java 6開始,注解處理器正式標準化,apt工具也被直接集成在javac當中。

我們還是回到如何編寫編譯時注解處理器這個話題上,編譯一個編譯時注解處理主要分兩步:

1、繼承AbstractProcessor,實現自己的注解處理器

2、注冊處理器,并打成jar包

首先來看一下一個標準的注解處理器的格式:

  1. public class MyAnnotationProcessor extends AbstractProcessor { 

  2.  

  3.  

  4.     @Override 

  5.     public Set<String> getSupportedAnnotationTypes() { 

  6.         return super.getSupportedAnnotationTypes(); 

  7.     } 

  8.  

  9.  

  10.     @Override 

  11.     public SourceVersion getSupportedSourceVersion() { 

  12.         return super.getSupportedSourceVersion(); 

  13.     } 

  14.  

  15.  

  16.     @Override 

  17.     public synchronized void init(ProcessingEnvironment processingEnv) { 

  18.         super.init(processingEnv); 

  19.     } 

  20.  

  21.  

  22.     @Override 

  23.     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 

  24.         return false; 

  25.     } 


如何理解Java注解和注解解析器

編寫一個注解處理器首先要對ProcessingEnvironment和RoundEnvironment非常熟悉。接下來我們一覽這兩個類的風采.首先來看一下ProcessingEnvironment類:

public interface ProcessingEnvironment {       Map<String,String> getOptions();       //Messager用來報告錯誤,警告和其他提示信息     Messager getMessager();       //Filter用來創建新的源文件,class文件以及輔助文件     Filer getFiler();       //Elements中包含用于操作Element的工具方法     Elements getElementUtils();        //Types中包含用于操作TypeMirror的工具方法     Types getTypeUtils();       SourceVersion getSourceVersion();       Locale getLocale(); }

Element

element表示一個靜態的,語言級別的構件。而任何一個結構化文檔都可以看作是由不同的element組成的結構體,比如XML,JSON等。

對于java源文件來說, Element代表程序元素:包,類,方法都是一種程序元素 ,他同樣是一種結構化文檔:

package com.closedevice;             //PackageElement public class Main{                  //TypeElement     private int x;                  //VariableElement     private Main(){                 //ExecuteableElement     }     private void print(String msg){ //其中的參數部分String msg為TypeElement     } }

如何理解Java注解和注解解析器

TypeMirror

這三個類也需要我們重點掌握:

DeclaredType代表聲明類型:類類型還是接口類型,當然也包括參數化類型,比如Set<String>,也包括原始類型

TypeElement代表類或接口元素,而DeclaredType代表類類型或接口類型。

TypeMirror代表java語言中的類型.Types包括基本類型,聲明類型(類類型和接口類型),數組,類型變量和空類型。也代表通配類型參數,可執行文件的簽名和返回類型等。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;         }     } }

簡單來說,Element代表源代碼,TypeElement代表的是源碼中的類型元素,比如類。雖然我們可以從TypeElement中獲取類名,TypeElement中不包含類本身的信息,比如它的父類,要想獲取這信息需要借助TypeMirror,可以通過Element中的asType()獲取元素對應的TypeMirror。

然后來看一下RoundEnvironment,這個類比較簡單,一筆帶過:

public interface RoundEnvironment {     boolean processingOver();      //上一輪注解處理器是否產生錯誤     boolean errorRaised();      //返回上一輪注解處理器生成的根元素     Set<? extends Element> getRootElements();    //返回包含指定注解類型的元素的集合     Set<? extends Element> getElementsAnnotatedWith(TypeElement a);     //返回包含指定注解類型的元素的集合     Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a); }

Filer

Filer用于注解處理器中創建新文件,由于Filer用起來實在比較麻煩,后面我們會使用javapoet簡化我們的操作.

打包注解處理器的時候需要一個特殊的文件 javax.annotation.processing.Processor 在 META-INF/services 路徑下

如何理解Java注解和注解解析器

新建項目必要配置:

//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'

編譯時注解demo示例地址:https://gitee.com/yutg/apt.git

項目結構

--apt-demo ----bindview-annotation(Java Library)//注解定義 ----bindview-api(Android Library)//定義SDK接口方法 ----bindview-compiler(Java Library)//注解處理器相關操作及生成java文件 ----app(Android App)

感謝各位的閱讀,以上就是“如何理解Java注解和注解解析器”的內容了,經過本文的學習后,相信大家對如何理解Java注解和注解解析器這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

海伦市| 谷城县| 泰州市| 玛沁县| 江都市| 灌阳县| 武隆县| 武定县| 元谋县| 江油市| 萍乡市| 怀柔区| 紫金县| 响水县| 杂多县| 芜湖市| 炎陵县| 手游| 启东市| 个旧市| 扎兰屯市| 旬阳县| 高平市| 恩施市| 那曲县| 深水埗区| 威海市| 色达县| 台南县| 嘉义县| 玛曲县| 同德县| 岗巴县| 衡东县| 大石桥市| 交口县| 思茅市| 光山县| 长阳| 荣成市| 澄城县|