91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

注解處理器APT怎么生成

發布時間:2023-02-27 16:46:03 來源:億速云 閱讀:125 作者:iii 欄目:開發技術

今天小編給大家分享一下注解處理器APT怎么生成的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

一、定義

注解處理器(Annotation Processing Tool,簡稱APT),是JDK提供的工具,用于在編譯階段未生成class之前對源碼中的注解進行掃描和處理。處理方式大部分都是根據注解的信息生成新的Java代碼與文件。

APT使用相當廣泛,EventBus、ARouter、ButterKnife等流行框架都使用了該技術。

二、生成注解處理器

2.1 創建注解模塊

在項目中新建Module,選擇【Java or Kotlin Library】,名字和包名隨意填入,點擊Finish。

注解處理器APT怎么生成

② 在模塊中定義注解,注解保留范圍選擇SOURCE即可(因為APT是作用在源碼階段的,生成class之前),當然選擇CLASS和RUNTIME也可以,因為他們都包含SOURCE階段。

我們創建一個Test注解,并且包含int、String、Class、String[]四種類型。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Test {
    int key();

    String value() default "";

    Class clazz();

    String[] array() default {};
}

2.2 創建注解處理器模塊

① 在項目中新建Module,選擇【Java or Kotlin Library】,名字和包名隨意填入,點擊Finish。

注解處理器APT怎么生成

② 修改新建Module的build.gradle文件,根據是否使用Kotlin分為兩種情況

項目不使用Kotlin:

apply plugin: "java-library"

dependencies {
	// 注解模塊(必選)
    implementation project(':lib-annotation')
	// 注解處理器(必選)
    compileOnly 'com.google.auto.service:auto-service:1.0-rc7'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
    // 生成代碼方式之一(可選)
    implementation 'com.squareup:javapoet:1.13.0'
}

sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8

項目使用Kotlin:

apply plugin: "java-library"
apply plugin: "kotlin"
apply plugin: "kotlin-kapt"

dependencies {
	// 注解模塊(必選)
    implementation project(':lib-annotation')
    // 注解處理器(必選)
    kapt 'com.google.auto.service:auto-service:1.0-rc7'
    implementation 'com.google.auto.service:auto-service:1.0-rc7'
    // 生成代碼方式之一(可選)
    implementation 'com.squareup:javapoet:1.13.0'
}

sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8

2.3 創建注解處理器

在2.2的注解處理器模塊中新建類,繼承自AbstractProcessor類
使用@AutoService、@SupportedAnnotationTypes、@SupportedSourceVersion注解注釋該類,注解處理器即創建完成,具體如下:

// 讓該類擁有了獲取注解的能力(必選)
@AutoService(Processor.class)
// 設置該處理器支持哪幾種注解(必選)
// 字符串類型,例:com.kproduce.annotation.TEST
@SupportedAnnotationTypes({Const.CARD_ANNOTATION,Const.TEST_ANNOTATION})
// 源碼版本(可選)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TestAnnotationProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }
}

2.4 在app模塊中引入注解處理器

在主項目app模塊的build.gradle中引入注解處理器,使用kapt或annotationProcessor修飾,所以在gradle中看到這兩種修飾的項目就是注解處理器項目了。

dependencies {
	// 注解模塊
    implementation project(":lib-annotation")
    // 注解處理器模塊,以下二選一
    // 使用Kotlin選擇這種
    kapt project(":compiler")
    // 使用Java選擇這種
    annotationProcessor project(":compiler")
}

2.5 測試

經過上面的一系列操作,注解處理器已經注冊完成,在其process方法中會接收到想要處理的注解。

① 在項目中使用@Test注解

@Test(id = 100, desc = "Person類", clazz = Person.class, array = {"111", "aaa", "bbb"})
public class Person {

}

② 在注解處理器的init方法中,可以通過ProcessingEnvironment參數獲取Messager對象(可以打印日志),在process方法中獲取到注解后輸出日志查看被注解的類名。(注意:如果打印日志使用Diagnostic.Kind.ERROR,會中斷構建)

@AutoService(Processor.class)
@SupportedAnnotationTypes(Const.TEST_ANNOTATION)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TestAnnotationProcessor extends AbstractProcessor {

    private Messager messager;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        // 獲取Messager對象
        messager = processingEnv.getMessager();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    	// 獲取所有的被Test注解的對象,無論是類還是屬性都會被封裝成Element
        for (Element element : roundEnv.getElementsAnnotatedWith(Test.class)) {
            messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>GetAnnotation:" + element.getSimpleName());
        }
        return false;
    }
}

③ 構建項目,查看日志,成功獲取到注解注釋過的類名

注解處理器APT怎么生成

三、解析注解

獲取到了注解,我們看一下如何正確的拿到注解內的信息,在注解處理器中類、方法、屬性都會被形容成Element,由于我們定義的@Test只修飾類,所以Element也都是類。

@AutoService(Processor.class)
@SupportedAnnotationTypes(Const.TEST_ANNOTATION)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TestAnnotationProcessor extends AbstractProcessor {

    private Messager messager;
    // 這個是處理Element的工具
    private Elements elementTool;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
        elementTool = processingEnv.getElementUtils();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    	// 拿到被Test修飾的Element,因為我們只修飾類,所以拿到的Element都是類
        for (Element element : roundEnv.getElementsAnnotatedWith(Test.class)) {
        	// ===============獲取當前被修飾的類的信息===============
        	
        	// 獲取包名,例:com.kproduce.androidstudy.test
            String packageName = elementTool.getPackageOf(element).getQualifiedName().toString();
            // 獲取類名,例:Person
            String className = element.getSimpleName().toString();
			// 拼裝成文件名,例:com.kproduce.androidstudy.test.Person
            String fileName = packageName + Const.DOT + className;
            
			// ===============解析注解===============
			
			// 獲取注解
            Test card = element.getAnnotation(Test.class);
			// 注解中的int值
            int id = card.id();
            // 注解中的String
            String desc = card.desc();
            // 注解中的數組[]
            String[] array = card.array();
            // 獲取類有比較奇葩的坑,需要特別注意!
			// 在注解中拿Class然后調用getName()會拋出MirroredTypeException異常
			// 處理方式可以通過捕獲異常后,在異常中獲取類名
            String dataClassName;
            try {
                dataClassName = card.clazz().getName();
            } catch (MirroredTypeException e) {
                dataClassName = e.getTypeMirror().toString();
            }
        }
        return true;
    }
}

四、生成代碼

獲取到了注解信息,下一步就是根據注解生成Java代碼了。但是生成代碼的意義是什么呢?

答:WMRouter路由在獲取到了注解信息之后,會根據注解的內容生成路由注冊的代碼。 把一些復雜的可能寫錯的冗長的代碼變成了自動生成,避免了人力的浪費和錯誤的產生。下面是WMRouter生成的代碼:

注解處理器APT怎么生成

目前生成Java代碼有兩種方式,原始方式和JavaPoet。原始方式理解即可,咱們使用JavaPoet來解析注解、生成代碼。

4.1 原始方式

原始方式就是通過流一行一行的手寫代碼。
優點:可讀性高。
缺點:復用性差。

咱們看一下EventBus的源碼就能更深刻的理解什么是原始方式:

// 截取EventBusAnnotationProcessor.java中的片段
private void createInfoIndexFile(String index) {
    BufferedWriter writer = null;
    try {
        JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(index);
        int period = index.lastIndexOf('.');
        String myPackage = period > 0 ? index.substring(0, period) : null;
        String clazz = index.substring(period + 1);
        writer = new BufferedWriter(sourceFile.openWriter());
        if (myPackage != null) {
            writer.write("package " + myPackage + ";\n\n");
        }
        writer.write("import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;\n");
        writer.write("import org.greenrobot.eventbus.meta.SubscriberMethodInfo;\n");
        writer.write("import org.greenrobot.eventbus.meta.SubscriberInfo;\n");
        writer.write("import org.greenrobot.eventbus.meta.SubscriberInfoIndex;\n\n");
        writer.write("import org.greenrobot.eventbus.ThreadMode;\n\n");
        writer.write("import java.util.HashMap;\n");
        writer.write("import java.util.Map;\n\n");
        writer.write("/** This class is generated by EventBus, do not edit. */\n");
        writer.write("public class " + clazz + " implements SubscriberInfoIndex {\n");
        writer.write("    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;\n\n");
        writer.write("    static {\n");
        writer.write("        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();\n\n");
        writeIndexLines(writer, myPackage);
        writer.write("    }\n\n");
        writer.write("    private static void putIndex(SubscriberInfo info) {\n");
        writer.write("        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);\n");
        writer.write("    }\n\n");
        writer.write("    @Override\n");
        writer.write("    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {\n");
        writer.write("        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);\n");
        writer.write("        if (info != null) {\n");
        writer.write("            return info;\n");
        writer.write("        } else {\n");
        writer.write("            return null;\n");
        writer.write("        }\n");
        writer.write("    }\n");
        writer.write("}\n");
    } catch (IOException e) {
        throw new RuntimeException("Could not write source for " + index, e);
    } finally {
        if (writer != null) {
            try {
                writer.close();
            } catch (IOException e) {
                //Silent
            }
        }
    }
}

4.2 JavaPoet

JavaPoet是使用Java的API和面向對象思想來生成.java文件的庫。
優點:面向對象思想、復用性高。
缺點:學習成本高、可讀性一般。

因為學習點比較多,咱們僅對用到的API進行說明,其他的可以參考GitHub地址,里面有相當全面的教程。

4.2.1 生成代碼

我們先用JavaPoet生成一個HelloWorld類,下面是我們想要的Java代碼:

package com.example.helloworld;

public final class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, JavaPoet!");
  }
}

在JavaPoet中使用了面向對象的思想,萬物皆對象,方法和類也變成了對象。在類中代碼主要被分為了兩塊,一塊是方法(MethodSpec),一塊是類(TypeSpec)。

注解處理器APT怎么生成

接下來我們使用JavaPoet生成這段代碼,比較易懂。
① 先創建main方法的MethodSpec對象;
② 再創建HelloWorld類的TypeSpec對象,將main方法傳入。

// 創建main方法的MethodSpec對象
MethodSpec main = MethodSpec.methodBuilder("main")	// 方法名:main
    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)	// 方法修飾:public static
    .returns(void.class)	// 返回類型 void
    .addParameter(String[].class, "args")	// 參數:String[] args
    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")	// 內容System.out.println("Hello, JavaPoet!");
    .build();
    
// 創建HelloWorld類的TypeSpec對象,將main方法傳入
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")	// 類名:HelloWorld
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)	// 類修飾:public final
    .addMethod(main)	// 添加方法main
    .build();

// 構建生成文件,第一個參數為包名
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
    .build();
javaFile.writeTo(System.out);

經過以上步驟就可以生成HelloWorld類。

4.2.2 JavaPoet中的自定義類型

上面代碼中會發現幾個奇怪的類型寫在字符串中,詳細的講解可以查看GitHub地址。

$L:值,可以放各種對象,比如int,Object等。
$S:字符串。
$T:類的引用,會自動導入該類的包,比如new Date()中的Date。
$N:定義好的Method方法名,可以調用代碼中的其他方法。

4.2.3 各種案例

提供幾個案例,更好的理解JavaPoet,詳細的講解可以查看GitHub地址。

① 循環

void main() {
  int total = 0;
  for (int i = 0; i < 10; i++) {
    total += i;
  }
}

// JavaPoet方式 1
MethodSpec main = MethodSpec.methodBuilder("main")
    .addStatement("int total = 0")
    .beginControlFlow("for (int i = 0; i < 10; i++)")
    .addStatement("total += i")
    .endControlFlow()
    .build();
    
// JavaPoet方式 2
MethodSpec main = MethodSpec.methodBuilder("main")
    .addCode(""
        + "int total = 0;\n"
        + "for (int i = 0; i < 10; i++) {\n"
        + "  total += i;\n"
        + "}\n")
    .build();

② ArrayList

package com.example.helloworld;

import com.mattel.Hoverboard;
import java.util.ArrayList;
import java.util.List;

public final class HelloWorld {
  List<Hoverboard> beyond() {
    List<Hoverboard> result = new ArrayList<>();
    result.add(new Hoverboard());
    result.add(new Hoverboard());
    result.add(new Hoverboard());
    return result;
  }
}

// JavaPoet方式
ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
ClassName list = ClassName.get("java.util", "List");
ClassName arrayList = ClassName.get("java.util", "ArrayList");
TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard);

MethodSpec beyond = MethodSpec.methodBuilder("beyond")
    .returns(listOfHoverboards)
    .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList)
    .addStatement("result.add(new $T())", hoverboard)
    .addStatement("result.add(new $T())", hoverboard)
    .addStatement("result.add(new $T())", hoverboard)
    .addStatement("return result")
    .build();

③ 屬性

public class HelloWorld {
  private final String android;
  private final String robot;
}

// JavaPoet方式
FieldSpec android = FieldSpec.builder(String.class, "android")
    .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
    .build();
    
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC)
    .addField(android)
    .addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL)
    .build();

以上就是“注解處理器APT怎么生成”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

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

apt
AI

古蔺县| 屏南县| 济阳县| 静安区| 博白县| 新丰县| 定陶县| 龙南县| 平泉县| 南宁市| 抚顺市| 伊宁市| 巴南区| 福州市| 紫阳县| 瑞昌市| 兴文县| 大厂| 利辛县| 海兴县| 涟源市| 滦平县| 唐河县| 石阡县| 临漳县| 利辛县| 宁陵县| 茂名市| 深圳市| 潜江市| 安阳市| 临朐县| 甘谷县| 自贡市| 商丘市| 涟水县| 台北市| 商南县| 栾川县| 五家渠市| 沈丘县|