你真的會用Java自定義註解嗎?

註解: 註解爲咱們在代碼中添加信息提供一種形式化的方法,使咱們能夠在源碼、編譯時、運行時很是方便的使用這些數據。java

註解是在JAVA SE5中引入的,註解讓代碼更乾淨易讀而且能夠實現編譯期類型檢查等。當建立描述性質的類或接口時,若是有重複性的工做,就能夠考慮使用註解來簡化或自動化該過程。咱們可讓註解保存在源代碼中,而且利用Annotation API處理註解,獲得咱們想要的數據並加以處理,註解的使用比較簡單,JAVA SE5內置了3種:android

  • @Override 表示當前類中的方法將覆蓋父類中的方法,若是不寫也不會有錯,可是@Override能夠起到檢查做用,如方法名拼寫錯誤,編譯器就會報警告信息。
  • @Deprecated 表示被標註的方法已經被廢棄了,若是使用編譯器會發出警告信息。
  • @SuppressWarnings 關閉不當的編譯器警告信息。除非你肯定編譯器的警告信息是錯誤的,不然最好不要使用這個註解。

定義註解

先來看內置註解@Override是怎麼被定義的,它位於package java.lang之下:bash

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
複製代碼

@Target、@Retention稱爲元註解:元註解負責註解其餘的註釋,如: @Target定義聲明的註解的做用域(做用在類上仍是方法上) @Retention定義註解在哪一個級別可用,在源代碼中(SOURCE)、類文件中(CLASS)、仍是運行時(RUNTIME)。 除了@Target、@Retention還有@Documented及@Inherited,下面用一個表格來分別列出他們各自的做用:ide

@Retention做用範圍以下圖所示:

註解處理器

首先來自定義一個註解:函數

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationInfo {

String[] value();

int requestCode() default 0;
}
複製代碼
  • 註解中定義的方法沒有參數,且返回類型僅限於原始類型,字符串,枚舉,註解或以上類型的集合
  • 註解中定義的方法能夠有默認值

運行時解析註解

@Target(ElementType.METHOD)指明瞭咱們的註解是做用在方法上的 @Retention(RetentionPolicy.RUNTIME)表示註解在程序運行時期也會存在,即註解信息也會加載到虛擬機VM中,因此能夠經過反射來獲取註解的相關信息:工具

public class AnnotationExample {

/**
* 註解模擬請求權限
*/
@AnnotationInfo(value = {"android.permission.CALL_PHONE", "android.permission.CAMERA"}, requestCode = 10)
public void requestPermission() {
//其餘邏輯
}
}
複製代碼

接着來編寫一個運行時解析註解的Java類:AnnotationRuntimeProcessor.javaui

public class AnnotationRuntimeProcessor {

public static void main(String[] args) {
try {
//獲取AnnotationExample的Class對象
Class<?> cls = Class.forName("com.javastudy.Annotation.AnnotationExample");
//獲取AnnotationExample類中的方法
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
//過濾不含自定義註解AnnotationInfo的方法
boolean isHasAnnotation = method.isAnnotationPresent(AnnotationInfo.class);
if (isHasAnnotation) {
method.setAccessible(true);
//獲取方法上的註解
AnnotationInfo aInfo = method.getAnnotation(AnnotationInfo.class);
if (aInfo == null) return;
//解析註解上對應的信息
String[] permissions = aInfo.value();
System.out.println("value: " + Arrays.toString(permissions));

int requestCode = aInfo.requestCode();
System.out.println("requestCode: " + requestCode);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製代碼

上面的邏輯很簡單,反射拿到有註解對應類的Class對象,篩選含有註解的方法,最後獲取方法上的註解並解析,運行結果以下:spa

value: [android.permission.CALL_PHONE, android.permission.CAMERA]
requestCode: 10
複製代碼

編譯時解析註解

AbstractProcessor是javax下的API,java和javax都是Java的API(Application Programming Interface)包,java是核心包,javax的x是extension的意思,也就是擴展包。通常繼承AbstractProcessor須要實現下面的幾個方法:code

public class ProcessorExample extends AbstractProcessor {

@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
//processingEnvironment提供各類工具類 如Elements Filer Types SourceVersion等
super.init(processingEnvironment);
}

/**
* 掃描 評估和處理註解代碼 生成Java代碼
*
* @param set 註解類型
* @param roundEnvironment 有關當前和之前的信息環境 查詢出包含特定註解的被註解元素
* @return 返回true 表示註解已聲明 後續Processor不會再處理 false表示後續Processor會處理他們
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}

@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}

@Override
public Set<String> getSupportedAnnotationTypes() {
return super.getSupportedAnnotationTypes();
}
}
複製代碼
  • init(ProcessingEnvironment env): 每個註解處理器類都必須有一個空的構造函數。然而,這裏有一個特殊的init()方法,它會被註解處理工具調用,並輸入ProcessingEnviroment參數。ProcessingEnviroment提供不少有用的工具類Elements, Types和Filer。後面咱們將看到詳細的內容。
  • process(Set (? extends TypeElement) annotations, RoundEnvironment env): 這至關於每一個處理器的主函數main()。你在這裏寫你的掃描、評估和處理註解的代碼,以及生成Java文件。輸入參數RoundEnviroment,可讓你查詢出包含特定註解的被註解元素。後面咱們將看到詳細的內容。
  • getSupportedAnnotationTypes(): 這裏你必須指定,這個註解處理器是註冊給哪一個註解的。注意,它的返回值是一個字符串的集合,包含本處理器想要處理的註解類型的合法全稱。換句話說,你在這裏定義你的註解處理器註冊到哪些註解上。
  • getSupportedSourceVersion(): 用來指定你使用的Java版本。一般這裏返回SourceVersion.latestSupported()。然而,若是你有足夠的理由只支持Java 6的話,你也能夠返回SourceVersion.RELEASE_6。推薦使用前者。
相關文章
相關標籤/搜索