04注解处理器(apt)是什么?——《android打怪升级之旅》(代码片段)

老匡话Android 老匡话Android     2023-03-22     478

关键词:

感谢大家和我一起,在Android世界打怪升级!

上一篇讲完注解,这篇咱们科普一下注解的其中一种用途——注解处理器(APT),文章会手把手的帮助大家学会APT的使用。

一、定义

注解处理器(Annotation Processing Tool,简称APT),是JDK提供的工具,用于在编译阶段未生成class之前对源码中的注解进行扫描和处理。处理方式大部分都是根据注解的信息生成新的Java代码与文件。

APT使用相当广泛,EventBus、ARouter、ButterKnife等流行框架都使用了该技术。

二、生成注解处理器

2.1 创建注解模块

① 在项目中新建Module,选择【Java or Kotlin Library】,名字和包名随意填入,点击Finish。

② 在模块中定义注解,注解保留范围选择SOURCE即可,因为APT是作用在源码阶段的,生成class之前。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Test 

2.2 创建注解处理器模块

① 在项目中新建Module,选择【Java or Kotlin Library】,名字和包名随意填入,点击Finish。

② 修改新建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)
// 设置该处理器支持哪几种注解(必选)
@SupportedAnnotationTypes(Const.CARD_ANNOTATION,Const.TEST_ANNOTATION)
// 源码版本(可选)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TestAnnotationProcessor extends AbstractProcessor 

    @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 测试

① 在项目中使用@Test注解

@Test
public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    

② 在注解处理器的init方法中获取Messager对象,在process方法中获取到注解后输出日志查看被注解的类名。

@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;
    

③ 构建项目,查看日志,成功获取到注解注释过的类名

三、生成代码

获取到了注解信息,下一步就是根据注解生成Java代码了。目前生成Java代码有两种方式,原始方式和JavaPoet。

3.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
            
        
    

3.2 JavaPoet

JavaPoet是使用Java的API和面向对象思想来生成.java文件的库。GitHub地址,里面有相当全面的教程。

他的优点是面向对象、可扩展性高,缺点是学习成本高、可读性一般。
我们拿下面的代码举个简单的例子:

// 要生成的代码
package com.example.helloworld;

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

而使用JavaPoet生成代码,需要先写方法,然后再写类,再将方法放到类中。

// 使用JavaPoet生成main方法
// public static void main(String[] args) 
// 		System.out.println("Hello, JavaPoet!");
// 
MethodSpec main = MethodSpec.methodBuilder("main")
    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
    .returns(void.class)
    .addParameter(String[].class, "args")
    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
    .build();
    
// 使用JavaPoet生成HelloWorld类,将main方法放入其中
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
    .addMethod(main)
    .build();

// 构建生成文件
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
    .build();
javaFile.writeTo(System.out);

总结

最后咱们再总结一下APT。

  1. APT是JDK提供的工具,用于在编译阶段未生成class之前对源码中的注解进行扫描和处理。
  2. 获取到注解后可以使用原始方法与JavaPoet生成Java代码。

这样APT的介绍就结束了,希望大家读完这篇文章,会对APT有一个更深入的了解。如果我的文章能给大家带来一点点的福利,那在下就足够开心了。

下次再见!

注解深入浅出(二apt)

...ool,它是javac的一个工具,中文意思为编译时注解处理器。APT可以用来在编译时扫描和处理注解。通 查看详情

注解深入浅出(二apt)

...ool,它是javac的一个工具,中文意思为编译时注解处理器。APT可以用来在编译时扫描和处理注解。通过APT可以获取到注解和被注解对象的相关信息,在拿到这些信息后我们可以根据需求来自动的生成一些代码,省... 查看详情

android编译时注解处理apt(代码片段)

一、注解在使用Java语言开发的过程中,我们会经常看到各种各样的注解,@Override(表示方法的重写),@Deprecated(标记过时的元素方法,类或属性),@LayoutRes(表示的是布局资源),@IdRes(表示的是ID资源),@Drawa... 查看详情

android编译时注解处理apt(代码片段)

一、注解在使用Java语言开发的过程中,我们会经常看到各种各样的注解,@Override(表示方法的重写),@Deprecated(标记过时的元素方法,类或属性),@LayoutRes(表示的是布局资源),@IdRes(表示的是ID资源),@Drawa... 查看详情

aop之注解处理器apt在android中的finderview实际详解(代码片段)

...     具体可查看注解原理详解。      2、通过注解处理器APT来获取。   这里使用的是方式二。注解器处理是在build编译时执行的二原理    1、定义Method、Field的注解类分别为OnClick、BindView,分别应用于Method和Field  ... 查看详情

aop之注解处理器apt在android中的finderview实际详解(代码片段)

一前言     android中现今流行的各大框架,比如ButterFly、eventBus、OrmLite、Retrofit等都使用注解,注解是什么呢?注解就是元数据,可以理解为属性、方法、类等的一个说明,具体详解可百度,也可移步我... 查看详情

asm(代码片段)

...?分别解释下这几个名词APT:APT(AnnotationProcessingTool)即注解处理器,是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作... 查看详情

android编译时注解处理apt(代码片段)

一、注解在使用Java语言开发的过程中,我们会经常看到各种各样的注解,@Override(表示方法的重写),@Deprecated(标记过时的元素方法,类或属性),@LayoutRes(表示的是布局资源),@IdRes(表示的是ID资源),@Drawa... 查看详情

android编译时注解处理apt(代码片段)

一、注解在使用Java语言开发的过程中,我们会经常看到各种各样的注解,@Override(表示方法的重写),@Deprecated(标记过时的元素方法,类或属性),@LayoutRes(表示的是布局资源),@IdRes(表示的是ID资源),@Drawa... 查看详情

框架手写系列---apt注解处理器方式实现butterknife框架(代码片段)

一、ButterKnifeButterKnife作为常用框架之一,主要用于简化代码,减少重复代码。这里主要注重原理与核心,将分步骤手写它的核心代码。ButterKnife最常用的是去除代码中的findViewById,以注解的方式代替原有的代码... 查看详情

注解处理器apt在java中的实现(代码片段)

...文全名(AnnotationProcessorTool),即:注解处理器。它是 javac 的一个工具,这是Sun为了帮助注解的处理过程而提供的工具,apt被设计为操作Java源文件,而不是编译后的类。作用阶段示意图如下:具... 查看详情

你必须知道的aptannotationprocessorandroid-aptprovided自定义注解

...看看吧1、什么是APT?随着一些如ButterKnife,dagger等的开源注解框架的流行,APT的概念也越来越被熟知。annotationProcessor和android-apt的功能是一样的,它们是替代关系,在认识它们 查看详情

anroidapt(代码片段)

 前言APT:AnnotationProcessorTool(注解处理器)什么时注解处理器注解处理器是(AnnotationProcessor)是Javac的一个工具,其作用在编译期间,用来处理加了注解的代码,其会扫描编译其的源码获得加了特定注解的目标代码;注解处... 查看详情

anroidapt(代码片段)

 前言APT:AnnotationProcessorTool(注解处理器)什么时注解处理器注解处理器是(AnnotationProcessor)是Javac的一个工具,其作用在编译期间,用来处理加了注解的代码,其会扫描编译其的源码获得加了特定注解的目标代码;注解处... 查看详情

java编程基础:注解(annotationprocessingtool)注解处理器利用注解解读类属性生成xml文件

...生成额外的源文件和其他的文件(文件的具体内容由注解处理器的编写者决定),APT还会编译生成的源代码文件和原来的源文件,将它们一起生成class文件。Hibernate自动生成X 查看详情

apt案例之点击事件

目录介绍01.创建项目步骤1.1项目搭建1.2项目功能02.自定义注解03.创建Processor04.compiler配置文件05.编译jar06.如何使用07.编译生成代码08.部分源码说明8.1Process类-process方法8.2OnceProxyInfo代理类8.3OnceMethod类好消息博客笔记大汇总【16年3... 查看详情

android-asm字节码插桩与apt原理补充

...#xff0c;自动加载文件里所在的类。JavaC源码分析SPI机制注解处理器的很多东西都是依靠Round这个辅助工具返回值的作用?注解是否往下传递,如果是true,就不往 查看详情

java中的apt的工作过程(代码片段)

...作过程APT即AnnotatinoProcessingTool,他的作用是处理代码中的注解,用来生成代码,换句话说,这是用代码生成代码的工具,减少boilerplate代码.我们通过一个简单的例子来简单APT的工作过程,因为本文demo不设计ide及gradle等,请注意包名及import... 查看详情