使用Annotation Processor在編譯期生成模板代碼

1、Android注解快速入門和實(shí)用解析
2、Annotation Processor重復(fù)造輪子ARouter
3、編譯時(shí)注解帶你封裝微信支付

Annotation Processor Tool (APT)是一種處理注釋的工具,它對源代碼文件進(jìn)行檢測找出其中的Annotation,根據(jù)注解自動(dòng)生成代碼,如果想要自定義的注解處理能夠正常運(yùn)行,必須要通過APT工具來進(jìn)行處理。Java 的Annotation Processor是非常有用的功能,很多常用的庫和框架都使用了 Annotation Processor 來生成代碼,比如Butter Knife 、ARouter等都是用來生成 代碼的。

結(jié)構(gòu)化語言

 <html>
  <body>
  </body>
</html>

比如Html就是典型的Element 結(jié)構(gòu)化語言,body標(biāo)簽不能卸載html之上,因?yàn)檫@些是有規(guī)定的,那么Java也是一種結(jié)構(gòu)體語言:

package com.wfy.common;   // PackageElement 包元素節(jié)點(diǎn)

public class Order implements Serializable { // TypeElement 類或接口元素/節(jié)點(diǎn)

int orderId = 99; //VariableElement  字段,枚舉常量,方法或構(gòu)造函數(shù)參數(shù),局部變量或異常參數(shù)元素/節(jié)點(diǎn)

@Override
public String toString() {//ExecutableElement 方法元素/節(jié)點(diǎn)
    return "Order{" +
            "orderId=" + orderId +
            '}';
    }
}

java也是一種結(jié)構(gòu)體語言,來說說它的各個(gè)節(jié)點(diǎn)類:

  • PackageElement 一個(gè)包程序元素節(jié)點(diǎn);
  • TypeElement 類或接口元素節(jié)點(diǎn);
  • VariableElement 字段,枚舉常量,方法和構(gòu)造函數(shù)參數(shù),局部變量或異常參數(shù)元素節(jié)點(diǎn);
  • ExecutableElement 方法元素節(jié)點(diǎn);
  • TypeParameterElement 表示泛型類,接口,方法或構(gòu)造函數(shù)元素的形式類型參數(shù)節(jié)點(diǎn)。

常用API

屬性名 解釋
getEnclosedElements() 返回該Element直接包含的子元素,比如類可以包含構(gòu)造函數(shù)Element(PackageElement ),字段Element(VariableElement ),方法Element(ExecutableElement)等。
getEnclosingElement() 返回該Element的父Element, 如果Element是一個(gè)頂層類或者接口(TypeElement),就返回包Element(PackageElement),如果是包Element,就返回null。
getKind() 返回Element的類型,判斷是哪種Element。
getModifiers() 獲取修飾關(guān)鍵字,public static final等關(guān)鍵字。
getSimpleName() 獲取Element的名字,不帶包名。
getQuelfierdName() 獲取全名,如果是類的話,包含完整的包名路徑。
getParamters() 獲取方法的參數(shù)元素,每個(gè)元素是一個(gè)VariableElement。
getRetrunType() 獲取方法的返回值類型。
getConstantValue() 如果屬性變量被final修飾,則可以使用該方法獲取它的值。

最后我在介紹幾個(gè)常用的類:

Annotation Processor Tool (APT)

是一種處理注解的工具,它對源代碼文件進(jìn)行檢測找出其中的Annotation,根據(jù)注解自動(dòng)生成代碼,如果想要自定義注解處理器能夠正常運(yùn)行,必須要通過APT工具進(jìn)行處理。

本文通過如何使用注解來自動(dòng)生成Music和User類,這些類并沒有實(shí)際的使用意義;

注解:

Android Studio 中創(chuàng)建一個(gè) Java module,名字為 Annotation。在這個(gè)模塊中創(chuàng)建這個(gè)自定義的 MyAnnotation注解類:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
    * 只在源代碼中存在,編譯后沒有在字節(jié)碼中,所以最最終的運(yùn)行時(shí)是沒有影響的
   *
 */
@Target(ElementType.TYPE) //作用于類上
@Retention(RetentionPolicy.SOURCE)//在源碼可見
public @interface MyAnnotation {
}

注解處理器

注解處理器的功能就是用來讀取代碼中的注解然后來生成相關(guān)的代碼。

創(chuàng)建一個(gè) Java module 名字為 “compiler”。 該模塊在編譯的時(shí)候,來獲取哪些類使用了MyAnnotation 注解,并生成代碼。該模塊并不在 Android 項(xiàng)目中引用,只存在于編譯的時(shí)候。所以這個(gè)模塊的 Java 版本號可以隨意指定(Java 8 、9)

@SupportedAnnotationTypes("com.xx.lib.MyAnnotation")
public final class MyProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
    Collection<? extends Element> annotatedElements = env.getElementsAnnotatedWith(MyAnnotation.class);
    //轉(zhuǎn)換類型
    List<TypeElement> types = new ImmutableList.Builder<TypeElement>()
            .addAll(ElementFilter.typesIn(annotatedElements))
            .build();
    for (TypeElement type : types) {
        processType(type);
    }
    return true;
}

private void processType(TypeElement type) {
    String className = generatedClassName(type);
    StringBuffer sb = new StringBuffer();
    sb.append("public class " + type.getSimpleName() + "{");
    sb.append("private int age;");
    sb.append("private String name;");
    sb.append("}");
    String source = sb.toString();
    writeSourceFile(className, source, type);
}

private void writeSourceFile(String className, String text, TypeElement originatingType) {
    try {
        JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(className, originatingType);
        Writer writer = sourceFile.openWriter();
        try {
            writer.write(text);
        } finally {
            writer.close();
        }
    } catch (IOException e) {
    }
}

private String generatedClassName(TypeElement type) {
    String name = type.getSimpleName().toString();
    while (type.getEnclosingElement() instanceof TypeElement) {
        type = (TypeElement) type.getEnclosingElement();
        name = type.getSimpleName() + "_" + name;
    }
    String pkg = TypeUtil.packageNameOf(type);
    String dot = pkg.isEmpty() ? "" : ".";
    return pkg + dot + name;
}
}

對于該類有幾點(diǎn)要求:
1. 需要繼承至 AbstractProcessor
2. 需要使用類的全稱(包含包名)來指定其支持的注解類型(com.xx.lib.MyAnnotation)
3. 實(shí)現(xiàn) process() 函數(shù),在該函數(shù)中來處理所支持的注解類型并生成需要的代碼。

如果沒有其他處理器需要繼續(xù)處理該注解,則 process() 返回 true。針對我們這個(gè)情況,只有 MyProcessor需要處理 MyAnnotation注解,所以該函數(shù)返回 true。

注解處理器類編寫完后,需要?jiǎng)?chuàng)建一個(gè) java META_INF 文件來告訴系統(tǒng)具有注解處理功能。Java 代碼在編譯的時(shí)候,系統(tǒng)編譯器會查找所有的 META_INF 中的注冊的注解處理器來處理注解。

在 Android studio 的 compiler 項(xiàng)目中創(chuàng)建如下目錄:

compiler/src/main/resources/META_INF/services

在 services 目錄下面創(chuàng)建一個(gè)名字為 “javax.annotation.processing.Processor” 的文本文件:

該文件中每行一個(gè)注解處理器的全名:

com.xx.compiler.MyProcessor

注解處理器就創(chuàng)建好了

在 Android Studio 中使用

在 app/build.gradle 中添加前面創(chuàng)建的兩個(gè)模塊為依賴項(xiàng):

 compileOnly project(':Annotation')
 annotationProcessor project(':compiler')

注意上面 MyAnnotation項(xiàng)目使用的是 compileOnly 依賴,這是由于 compileOnly 中的代碼只在編譯的時(shí)候存在,并不會打包到最終的應(yīng)用中去,所以可以使用 compileOnly 。compiler 項(xiàng)目為注解編譯器,通過使用 插件來指定 annotationProcessor 。

接下來就可以使用了:

  @MyAnnotation
  public class User {
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容