android注解之APT和javapoet

前言

前面我们已经讲过注解的基本知识,对于注解还不太了解的,可以去看一下之前的文章,

android 注解详解_袁震的博客-CSDN博客。

之前我们在讲注解的时候,提到过APT和JavaPoet,那么什么是APT和JavaPoet呢?下面我们来详细讲解一下。

1,APT是什么?

APT,英文全称Annotation Processing Tool,是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码, 如果想要自定义的注解处理器能够正常运行,必须要通过APT工具来进行处理。 也可以这样理解,只有通过声明APT工具后,程序在编译期间自定义注解解释器才能执行。注意,是在编译期间

简单来讲,就是根据我们定义的注释规则,帮助我们生成代码,生成类文件。

2,APT中的元素

在APT中,它会分为包元素,类元素,属性元素,方法元素。那么,这些元素的意义是什么呢?

 首先,我们需要明白APT真正的作用是什么。就拿ButterKnife来说,他真正要实现的就是我们通过BindView,把id传给注解,然后就会在编译时动态生成很多类,专门去处理你绑定的这些id,从而达到你只需要几行代码就能实现绑定,点击事件等功能。那为什么不能直接就写好类去处理,而非要到编译期去自动生成类处理呢?因为它不知道你会传哪些id,所以需要动态的生成。

所以,我认为APT的主要作用就是帮你动态生成类。

import androidx.appcompat.app.AppCompatActivity;//PackageElement 包元素/节点public class MainActivity2 extends AppCompatActivity { // TypeElement 类元素/节点private int a;// VariableElement 属性元素/节点@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {// ExecuteableElement 方法元素/节点super.onCreate(savedInstanceState);}
}

这些元素的意义就是,它们会提供相关信息,来帮助你后面生成类。

包元素
PackageElement
表示一个包程序元素。提供对有关包及其成员的信息的访问
方法元素
ExecutableElement
表示某个类或接口的方法、构造方法或初始化程序(静态或实例)
类元素
TypeElement
表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问
属性元素
VariableElement
表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数

3,APT中常用的API

在AbstractProcessor中,有两个方法是核心方法:

    
//初始化工作,主要做一些准备工作
public synchronized void init(ProcessingEnvironment processingEnv) {}//处理注解 核心方法
//annotations 使用了支持处理注解的节点集合
//roundEnv 当前或是之前的运行环境,可以通过该对象查找的注解
//return true 表示后续处理器不会再处理(已经处理完成)
public abstract boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv);

上面ProcessingEnvironment中常用的api如下:

getElementUtils()获取操作Element的工具类
getMessager()获取Messager,用来打印日志相关信息
getFiler()获取文件生成器,类等最终要生成的文件,都是通过生成器生成的
getTypeUtils()获取类信息的工具类,用于操作TypeMirror的工具方法
getOptions()主要用来接收应用传过来的数据

上面RoundEnvironment常用api如下:

//获取所有被@YuanZhen注解的元素集合
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(YuanZhen.class);

Element常用api如下:

getEnclosedElements()
返回该元素直接包含的子元素
getEnclosingElement()
返回包含该element的父element,与上一个方法相反
getKind()
返回element的类型,判断是哪种element
getModifiers()
获取修饰关键字,入public static final等关键字
getSimpleName()
获取名字,不带包名
getQualifiedName()
获取全名,如果是类的话,包含完整的包名路径
getParameters()
获取方法的参数元素,每个元素是一个VariableElement
getReturnType()
获取方法元素的返回值
getConstantValue()
如果属性变量被final修饰,则可以使用该方法获取它的值

4,APT环境搭建

4.1新建注解javalib,命名为compiler

4.2在compiler的buidl.gradle中添加依赖,用来注册注解处理器

compileOnly'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'

4.3新建AnnotationProcessor

新建MyAnnotationProcessor类,继承AnnotationProcessor

// AutoService则是固定的写法,加个注解即可
// 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册
// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件
@AutoService(Processor.class)// 允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({"com.yuanzhen.yuanzhenannotation.YuanZhen"})// 指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class MyAnnotationProcessor extends AbstractProcessor {@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment       roundEnvironment) {return false;}
}

4.4新建注解javalib  命名为yuanzhenannotation

4.5创建自己的注解

@Target(ElementType.TYPE) //作用与类上
@Retention(RetentionPolicy.SOURCE) //在编译时期生效
public @interface YuanZhen {String value();//一个默认值
}

4.6添加依赖

在compiler中依赖yuanzhenannotation

在app中添加依赖:

4.7在app中使用注解:

@YuanZhen("study")
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}
}

至此,APT环境搭建完成

5,APT获取注解

在MyAnnotationProcessor类中:

// AutoService则是固定的写法,加个注解即可
// 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册
// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件
@AutoService(Processor.class)// 允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({"com.yuanzhen.yuanzhenannotation.YuanZhen"})// 指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class MyAnnotationProcessor extends AbstractProcessor {private Messager messager;// 用来打印日志相关信息private Elements elementUtils;// 操作Element的工具类(类,函数,属性,其实都是Element)private Filer filer;//文件生成器, 类 资源 等,就是最终要生成的文件 是需要Filer来完成的private Types typeUtils;// type(类信息)的工具类,包含用于操作TypeMirror的工具方法@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);messager = processingEnv.getMessager();elementUtils = processingEnv.getElementUtils();filer = processingEnv.getFiler();typeUtils = processingEnv.getTypeUtils();}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {//因为javalib没有Log,所以我们使用messager来打印messager.printMessage(Diagnostic.Kind.NOTE,"aaaaaaZZZZZ");//打印 //获取所有被@YuanZhen注解的元素集合Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(YuanZhen.class);for (Element element : elements) {String className =element.getSimpleName().toString();//获取元素名messager.printMessage(Diagnostic.Kind.NOTE,"-----="+className);//打印类名YuanZhen annotation = element.getAnnotation(YuanZhen.class);//获取注解messager.printMessage(Diagnostic.Kind.NOTE,"-----value="+annotation.value());//打印注解参数}return true;}
}

具体的api在上文已经有个介绍,下面看Build日志输出:

参数study已经传递过来,类名MainActivity也已经获取到。

6,传统的生成类的方式

上面已经完成了APT环境的配置,下面就是动态生成类了,在javapoet之前,传统的生成类的方式就是采用字符串拼接的样式。最典型的应用就是EventBus。

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}}}
}

通过上面可以看到,Eventbus是通过字符串拼接的形式来生成类的,这种方式虽然比较简单,但是也不是一件轻松的体力活,不符合OOP的编程思想。下面我们就来介绍一下现在最流行的写法,采用javapoet。

7,JavaPoet简介

JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件,这个框架功能非常实用,也是我们习惯的Java面向对象OOP语法, 可以很方便的使用它根据注解生成对应代码。 通过这种自动化生成代码的方式, 可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。

8,JavaPoet中常用的类

MethodSpec代表一个构造函数或方法声明
TypeSpec代表一个类,接口,或者枚举声明
FieldSpec代表一个成员变量,一个字段声明
JavaFile包含一个顶级类的Java文件
ParameterSpec用来创建参数
AnnotationSpec用来创建注解
ClassName用来包装一个类
TypeName类型,如在添加返回值类型是使用 TypeName.VOI

$S 字符串,如:$S, ”hello

$T 类、接口,如:$T, MainActivit

9,JavaPoet生成类的思想

传统模式生成类的思想是先写包,然后类,然后方法。

JavaPoet的思想恰恰相反,它是先方法,然后类,然后包。

10,JavaPoet 使用

10.1 导包

在complier的build.gradle里面导包

 implementation "com.squareup:javapoet:1.9.0"

10.2写出要生成的class

/**
package com.yuanzhen.yuanzhenannotation;public class MyClass {public static void main(String[] args) {System.out.println("Hello, yuanzhen");}
}
*/

10.3 通过javapoet生成该类

 // 1.方法 
MethodSpec mainMethod = MethodSpec.methodBuilder("main")//添加方法名.addModifiers(Modifier.PUBLIC, Modifier.STATIC)//添加修饰符.returns(void.class)//添加返回值.addParameter(String[].class, "args")//添加方法参数.addStatement("$T.out.println($S)", System.class, "Hello, YuanZhen!")//添加内容.build();// 2.类TypeSpec helloWorld = TypeSpec.classBuilder("MyClass1")//添加类名.addModifiers(Modifier.PUBLIC, Modifier.FINAL)//添加修饰符.addMethod(mainMethod)//添加方法.build();// 3.包JavaFile packagef = JavaFile.builder("com.yuanzhen.apt1", helloWorld).build();// 去生成try {packagef.writeTo(filer);messager.printMessage(Diagnostic.Kind.NOTE, "success...");} catch (IOException e) {e.printStackTrace();messager.printMessage(Diagnostic.Kind.NOTE, "error...");}

10.4在app build中查看生成的文件

11,总结

关于APT和JavaPoet的使用就讲完了,利用这个技术,我们可以实现很多强大的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/133197.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工智能安全-6-SQL注入检测

0 提纲 概述SQL注入方法SQL注入的检测方法SQL语句的特征提取天池AI上的实践 1 概述 SQLIA&#xff1a;SQL injection attack SQL 注入攻击是一个简单且被广泛理解的技术&#xff0c;它把 SQL 查询片段插入到 GET 或 POST 参数里提交到网络应用。 由于SQL数据库在Web应用中的…

Qt(day1)

思维导图 Qt实现第一个web的基础页面 #include "mywnd.h" #include "ui_mywnd.h" #include<QLabel> #include<QLineEdit> #include<qpushbutton.h>MyWnd::MyWnd(QWidget *parent): QMainWindow(parent), ui(new Ui::MyWnd) {ui->set…

Redis的介绍、安装和配置

文章目录 一、redis官网二、redis是什么三、能干嘛总体功能一图概述优势小总结 三、Redis的下载与安装Redis的安装Redis迭代演化和Redis7新特性新特性部分说明Redis的安装 安装步骤总结Redis的卸载 一、redis官网 https://redis.io/ 中文官网&#xff1a;http://www.redis.cn/…

【【萌新编写risc-v之软件的学习使用】】

萌新编写risc-v软件的学习使用 对于vscode我其实没什么好说的 就是先配置好环境 其实vscde作为一个编译器的软件 其实并不需要指望能往里面加载多少功能 我们需要做的就是赋予编辑器更好用的功能和体验 有些人会在vscode里面甚至加入波形 仿真 我觉得不如交给 vivado来用 在vs…

【计算机网络】传输层协议——TCP(下)

文章目录 1. 三次握手三次握手的本质是建立链接&#xff0c;什么是链接&#xff1f;整体过程三次握手过程中报文丢失问题为什么2次握手不可以&#xff1f;为什么要三次握手&#xff1f; 2. 四次挥手整体过程为什么要等待2MSL 3. 流量控制4. 滑动窗口共识滑动窗口的一般情况理解…

【面试经典150 | 双指针】两数之和

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;暴力枚举方法二&#xff1a;哈希表方法三&#xff1a;二分法方法四&#xff1a;双指针 知识回顾写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢…

【推荐】赴日IT课程 做赴日IT我该学什么?

许多想要做赴日IT的朋友问我说&#xff0c;我都该准备什么&#xff0c;或者我该学些什么才能达到可以做赴日程序员的水平呢&#xff1f;今天我就来跟大家聊一下这个问题。要说做准备&#xff0c;你需要有全日制大专及以上的学历才能获得赴日的资格&#xff0c;如果没有我们就先…

Scrum敏捷开发端到端管理流程

Leangoo领歌是Scrum中文网&#xff08;scrum.cn&#xff09;旗下的一款永久免费的敏捷研发管理工具。 Leangoo领歌覆盖了敏捷研发全流程&#xff0c;它提供端到端敏捷研发管理解决方案&#xff0c;包括小型团队敏捷开发&#xff0c;规模化敏捷SAFe&#xff0c;Scrum of Scrums…

Vue3 Ajax(axios)异步

文章目录 Vue3 Ajax(axios)异步1. 基础1.1 安装Ajax1.2 使用方法1.3 浏览器支持情况 2. GET方法2.1 参数传递2.2 实例 3. POST方法4. 执行多个并发请求5. axios API5.1 传递配置创建请求5.2 请求方法的别名5.3 并发5.4 创建实例5.5 实例方法5.6 请求配置项5.7 响应结构5.8 配置…

PAL/NTSC/1080I和interlaced scan(隔行扫描)

目录 1.PAL/NTSC和1080I 2.PAL/NTSC/1080I的timing 2.1 NTSC的垂直同步 2.2 PAL的垂直同步​编辑 2.3 1080i50FPS的vic20的时序 3.interlaced video timing实现说明 1.PAL/NTSC和1080I NTSC 和PAL 是两种不同视讯标准, 两种都是CRT时代遗留下的产物, 也都使用Interlace技术…

3D WEB轻量化引擎HOOPS Commuicator技术概览(一):数据导入与加载

HOOPS Communicator是一款功能强大的SDK&#xff0c;适用于基于Web的高级工程应用程序&#xff0c;代表HOOPS Web平台的Web开发组件。使用HOOPS Communicator&#xff0c;您可以构建一个在 Web浏览器中提供3D模型的Web应用程序。 HOOPS Communicator可以本地加载多种模型格式。…

Postman应用——初步了解postman

Postman 是一个用于构建和使用 API 的 API 平台&#xff0c;Postman 简化了 API 生命周期的每个步骤并简化了协作&#xff0c;可以更快地创建更好的 API。 Postman 包含一个基于Node.js的强大的运行时&#xff0c;允许您向请求&#xff08;request&#xff09;和分组&#xff…

今晚8点,iPhone15开启预售

北京时间9月15日晚8点&#xff0c;备受全球果粉期待的苹果iPhone15系列手机正式开启预售。此次预售在苹果官网Apple Store在线商店、天猫Apple Store官方旗舰店以及Apple Store官方在线商店微信小程序同步进行。 今年苹果公司将Apple Store在线商店、天猫Apple Store官方旗舰店…

【JAVA】项目部署

IDEA部署maven&#xff1a;https://www.cnblogs.com/ckfuture/p/15821541.html MySQL数据库安装&#xff1a;https://blog.csdn.net/SoloVersion/article/details/123760428 SQLyog安装&#xff1a; https://blog.csdn.net/qq_43543789/article/details/107997510 git安装&a…

JDBC基本概念

什么是JDBC JDBC概念 JDBC&#xff08;Java DataBase Connectivity&#xff09;是一套统一的基于Java语言的关系数据库编程接口规范。 该规范允许将SQL语句作为参数通过JDBC接口发送给远端数据库&#xff0c; …

电子技术基础(三)__第1章电路分析基础_第13篇__正弦交流电的相量表示

本文讲解 正弦交流电的稳态分析————正弦量的相量表示 一 基本概念 接下来&#xff0c; 注意: 大写字母 上 加点 表示相量 例如&#xff1a; 因为这里有 I m I_{m} Im​ 是幅值&#xff0c; 所以此相量称为幅值相量。 相量 其实就是一个复数&#xff0c; 表示正弦量的复…

弗恩基 Flex-N-Gate EDI 需求分析

弗恩基Flex-N-Gate是一家总部位于美国伊利诺伊州的汽车零部件制造公司。该公司成立于1956年&#xff0c;由亿万富翁企业家 Shahid Khan 创办。Flex-N-Gate 主要专注于设计、制造和供应汽车外部和内部零部件&#xff0c;包括前后保险杠系统、灯具、车门零件、悬挂系统等。 该公…

IOMesh 为 KubeVirt 提供高效稳定的持久化存储支持(附用户实践)

7 月 11 日&#xff0c;KubeVirt 社区正式宣布发布 Kubernetes 原生虚拟机管理插件 KubeVirt v1.0。这一版本发布不仅标志着 KubeVirt 已进化为生产就绪的虚拟机管理解决方案&#xff0c;也为正在使用虚拟化环境的用户提供了更多元的云化转型路线&#xff1a;搭配 Kubernetes 持…

【结构型】享元模式(Flyweight)

目录 享元模式(Flyweight)适用场景享元模式实例代码&#xff08;Java&#xff09; 享元模式(Flyweight) 运用共享技术有效地支持大量细粒度的对象。&#xff08;业务模型的对象进行细分得到科学合理的更多对象&#xff09; 适用场景 一个应用程序使用了大量的对象。完全由于…

概率统计笔记:从韦恩图的角度区分 条件概率和联合概率

联合概率&#xff1a;两个或多个事件同时发生的概率。用 P(A∩B) 或 P(A,B) 表示 条件概率&#xff1a;在已知某个事件发生的条件下&#xff0c;另一个事件发生的概率。用P(A∣B) 表示在事件 B 发生的条件下&#xff0c;事件 A 发生的概率。 不难发现联合概率的样本空间更大&am…