關於註解,你須要知道的一些知識:
Annotation註解(一)- 基礎
Annotation註解(二)- 進階 - 自定義ButterKnifejava
這篇博客,主要講解關於註解的一些基本知識,包括註解的概念、分類、做用,常見註解的定義及其解析方式等。android
尊重原創,轉載請註明出處 https://segmentfault.com/a/11...
本文出自 強哥大天才的博客segmentfault
關於Annotation註解的概念,咱們能夠看下官方的解釋:數組
Annotations, a form of
metadata
;provide data
about a program that is not part of the program itself.
Annotationshave no direct effect
on the operation of the code they annotateapp
Annotation註解是Java中的一種元數據,它能夠往源代碼中添加額外的數據,而且對註解的代碼無直接影響;包名、類、成員方法、成員屬性、參數均可以被註解。框架
註解可讓咱們的代碼更加簡潔,而且增長代碼的複用性,避免冗餘代碼。
若是細分起來,註解主要有3個做用:ide
提示信息
、檢查錯誤生成一些額外的代碼
、或者Java/XML等文件獲取註解信息
1. 標準Annotation
標準Annotation是指Java自帶的Annotation。
eg:@Override[重寫]、@Deprecated[不鼓勵使用]、@SuppressWarnings[忽略警告]等。工具
2. 元Annotation
元Annotation是指註解Annotation的Annotation。
eg:@Retention, @Target, @Inherited, @Documented等。性能
3. 自定義Annotation
咱們能夠利用元Annotation來自定義一些本身的Annotation。gradle
元Annotation是指註解Annotation的Annotation。
@Documented
:Annotation是否會保存到JavaDoc文檔中@Inherited
:Annotation是否能夠被繼承,默認是false@Target(ElementType[] types)
:Annotation能夠用來註解哪些元素ElementType:元素類型,好比類、方法、變量、參數等
@Retention(RetentionPolicy ploicy)
:Annotation的保留策略RetentionPolicy.SOURCE:Annotation僅保留在java源碼中
- 大都爲Mark Annotation,作一個警示、校驗的做用
RetentionPolicy.CLASS:Annotation保留在java源碼、class文件中
- 默認的保留策略,通常配合Processor註解處理器,在構建時動態生成代碼
RetentionPolicy.RUNTIME:Annotation保留在java源碼、class文件、運行時
- 與CLASS的區別是,這類Annotation在運行時會被加載到JVM中,所以咱們能夠在程序運行的過程當中動態獲取到Annotation的相關信息
咱們先來看一下一個最爲基礎的Annotation示例代碼(這是一個自動生成類頭信息的一個註解)
調用
@ClassInfo ( author = "xzqbetter@163.com", date = "11/17/2017", currentRevision = 6, reviewers = {"Alice", "Bob", "Cindy"} // Note array notation ) public class Test1 { // class code goes here }
定義
public @interface ClassInfo { String author(); String date(); int currentRevision() default 1; String[] reviewers(); // Note use of array }
解釋
1.註解的定義:能夠看到,咱們利用@interface,定義了一個註解類
,以後就能夠在代碼中使用這個註解
2.參數的定義:在註解類內部,咱們聲明瞭不少抽象方法,來定義註解所需的參數
沒有方法體、沒有參數、不能拋異常
,而且只能用public abstract進行修飾
default
來指定默認值value()
代替(在使用的時候無需寫參數名)classInfo.author()
來獲取參數值完成自定義Annotation後,咱們還須要知道,針對這些註解,咱們要作哪些相關的處理,這就涉及到了Annotation的解析操做。
解析Annotation,一般分爲:對運行時Annotation的解析、對編譯時Annotation的解析;
解析Annotation,其實就是如何從代碼中找到Annotation,一般咱們的作法是:
反射
的方式獲取Annotation,運行時Annotation的解析方式apt
工具獲取Annotation,編譯時Annotation的解析方式JavaPoet API
反射的解析方式,一般運用在運行時Annotation的解析。
反射是指:利用Class、Field、Method、Construct等reflect對象,獲取Annotation:
field.getAnnotation(Annotation.class)
:獲取某個Annotationfield.getAnnotations()
:獲取全部的Annotationfield.isAnnotationPresent(Annotation.class)
:是否存在該Annotation這樣,咱們就能夠在程序運行過程當中,動態的獲取Annotation的信息。
藉助反射進行解析,這在必定程度上會影響程序性能
概念
APT:是一個註解處理工具 Annotation Processing Tool
做用:利用apt,咱們能夠找到源代中的註解,並根據註解作相應的處理
生成
額外的源文件或其餘文件編譯
生成的源文件和原來的源文件,一塊兒生成class文件利用APT,在編譯時生成額外的代碼,不會影響性能,只是影響項目構建的速度
Android Studio配置
使用apt工具前,須要對gradle作一些基本的配置:
1.在project的build.gradle中,添加apt的依賴
dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' }
2.在module的build.gradle中,使用apt插件
apply plugin: 'com.neenbedankt.android-apt'
JavaPoet:是一個自動生成Java代碼的Java API。
在使用前,咱們須要導入相關的依賴:
compile 'com.squareup:javapoet:1.7.0'
JavaPoet主要有如下幾個關鍵類:
JavaFile
:生成Java文件TypeSpec
:定義類MethodSpec
:定義方法MethodSpec
MethodSpec主要用來定義方法。
MethodSpec mainSpec = MethodSpec.methodBuilder("main") // 方法名 .addModifiers(Modifier.PUBLIC, Modifier.STATIC) // 修飾符 .addParameter(String[].class, "args") // 參數 .returns(TypeName.VOID) // 返回值 .addStatement("$T.out.println($S)", System.class, "Hello,World!!") // 具體代碼 .build(); // 添加代碼的方法 mainBuilder.addCode("System.out.println(\"Hello,World!\")"); mainBuilder.addStatement("$T.out.println($S)",System.class,"Hello, World!"); mainBuilder.addStatement("activity.$L= ($T) activity.findViewById($L)", element, ClassName.get(member.asType()), injectViewAnno.value() ); // 這裏的element表示一個成員變量, injectViewAnno是一個Annotation, value是註解的參數值
方法名:
methodBuilder()
修飾符:addModifiers(Modifier)
返回值:return(TypeName)
參數:addParameter(Class, name)
添加代碼:addStatement
末尾會自動添加換行符,addCode
末位不會自動添加換行符
-$T
:表示須要import的類
-$S
:表示字符串,會自動添加雙引號
-$L
:表示變量,不帶雙引號
TypeSpec
TypeSpec主要用來定義類。
// 外部類 TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld") // 類名 .addModifiers(Modifier.PUBLIC, Modifier.FINAL) // 修飾符 .addMethod(mainSpec) // 方法 .build(); // 內部類 TypeSpec typeSpec = TypeSpec.anonymousClassBuilder("innerClass") // 內部類的類名 .addSuperinterface(OutClass.class) // 外部類的Class .build();
JavaFile
JavaFile是真正生成Java文件的核心類。
try { JavaFile javaFile = JavaFile.builder("com.example.seven", typeSpec).build(); // 指定包名+類 javaFile.writeTo(processingEnv.getFiler()); // 固定寫法 } catch (IOException e) { e.printStackTrace(); }
運行時Annotation,是指@Retention爲RetentionPolicy.RUNTIME的註解。
對於這類註解,咱們一般配合反射
進行解析。
運行時Annotation的解析,須要藉助反射進行解析,這在必定程度上會影響程序性能。
早期的依賴注入框架,大都屬於運行時處理。
示例代碼
public static void main(String[] args) { try { Class clazz = Class.forName("com.example.Runtime"); for (Method method : clazz.getMethods()) { MethodAnno methodAnno = method.getAnnotation( MethodAnno.class); if (methodAnno != null) { System.out.println("method name:" + method.getName()); System.out.println("method author:" + methodAnno.author()); System.out.println("method version:" + methodAnno.version()); System.out.println("method date:" + methodAnno.date()); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } }
編譯時Annotation,是指@Retention爲RetentionPolicy.CLASS的註解。
對於這類註解,咱們一般藉助apt工具
、註解處理器Processor
進行解析。
編譯時Annotation,利用apt工具,在編譯時,自動生成一些額外的代碼,相比運行時Annotation更加高效。
定義一個編譯時Annotation的標準流程以下:
下面,咱們將一一進行講解
參考,上面的【自定義註解】部分
概念
註解處理器
是一個繼承自AbstractProcessor
的一個Java類,內部主要定義了:
getSupportedAnnotationTypes
方法進行指定process
方法進行指定注意:AbstractProcessor只能用在Java項目中,在Android項目中沒法使用,由於Android內部刪除了這個類;所以若是想要在Android項目中,使用這個類,須要新建一個
Java Library
庫。
示例代碼
@AutoService(Processor.class) public class AutoCreateProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes() { // 返回的Set集合,是註解類ClassName的集合 // AutoCreate是一個自定義的Annotation return Collections.singleton(AutoCreate.class.getCanonicalName()); } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { // 利用JavaPoet進行相關處理 return false; } }
解釋
getSupportedAnnotationTypes
Set集合
的形式返回className
process
實現註解處理器,主要是利用JavaPoet
對process部分的代碼進行完善補充,在下一個博客中,咱們將會詳細介紹。
註冊註解處理器,就是告訴APT這個類是一個Annotation Processor
咱們能夠藉助AutoService
進行快速註冊:
compile 'com.google.auto.service:auto-service:1.0-rc2' @AutoService(Processor.class)
完成以上步驟,咱們build下工程,就會在build目錄下自動生成對應的java/class文件