概述
因项目需要,本人最近实现了一个自定义的注解处理器
在这里把这个过程中的一些经验分享出来
1. 写文件
得到被注解修饰的对象(如 Class Method Field)等,方法比较固定
Set<? extends Element> genElements = roundEnv.getElementsAnnotatedWith(xxx.class)
但是写文件的时候,有几个方向,我知道的就有几个
1.1 使用 javax.annotation.processing.Filer
大部分教程都是用的这个类,如以下的示例代码(仅供参考)
try {
JavaFileObject jfo = mFiler.createSourceFile(newFullPackage, new Element[]{});
Writer writer = jfo.openWriter();
writer.write(sb.toString());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
使用这个类的好处是:Maven
Gradle
都对这个类进行了支持
新生成的文件会被写入到特定目录,Maven
Gradle
在编译的时候会自动编译该目录,简单、好用
生成文件内容,这里又有几个路线,文章后面再说
1.2 OutputStream
反正就是新建文件嘛,只要能实现这个目的就行,所以OutputStream
也可以
指定好输出的目录即可
1.3 lombok
比较牛逼,走的比较远的是 lombok
github
Java
提供的注解处理器其实只能新建文件,不允许修改已有的文件或者字节码
但是 lombok
实现了 两套抽象语法树 AST
,分别是 Java
和 Eclipse
在注解处理器被运行的时候,去修改这个语法树,再输出到原文件
这里有个博客有一些介绍
2. 文件内容
生成新的文件的内容,也有几种选择
2.1 代码写死
这是最简单,最容易想到的
适用于输出内容大部分固定,一小部分动态变化的场景
2.2 javapoet
生成文件内容一大利器
这是源码github
Maven
依赖
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.11.1</version>
<scope>compile</scope>
</dependency>
用法比较简单,直接看 他们的ReadMe
就可以了
2.3 替换
这个是我自己想到,并且在使用的
适用场景是:原文件设置一些占位符,注解处理器去替换、填充这些占位符,输出多份内容
比如:注解替换 场景
实现思路:得到注解所在类对象的 BaseFileObject
然后调用 openReader
得到 InputStreamReader
,就能读到文件内容了
再把文件内容遍历替换一下,再写到文件即可
下面以 一个 只能写在 字段上的注解为例,举例说明如何读文件
这是注解
@Retention(SOURCE)
@Target(FIELD)
public @interface FanggeekId
这是部分代码
这里的一些类型强转,方法 都是我单步调试得到的,不保证通用
环境是 jdk 1.8
gradle 4.9
maven 3.5.4
// 获取使用了注解 @FanggeekId 的元素
Set<? extends Element> genElements = roundEnv.getElementsAnnotatedWith(FanggeekId.class);
for (Element e : genElements) {
// 现在注解限制了只能写在 字段上,所以这里的 Element类型是确定的
// e 是 Symbol$VarSymbol ,它的上级,owner, 是 Symbol$ClassSymbol
System.out.println("字段名是 >>> " + e.getSimpleName());
System.out.println("字段类型是 >>> " + e.asType().toString());
VarSymbol fieldElement = (VarSymbol) e;
ClassSymbol javaElement = (ClassSymbol) fieldElement.location();
System.out.println("javaElement ---> " + javaElement.toString());
System.out.println("javaElement class ---> " + javaElement.getClass());
//这里需要注意,在 maven 执行 注解处理器的时候, classfile 和 sourcefile 都会被设值
//而 gradle 只会设置 sourcefile, classfile是null
BaseFileObject javaFileObject = (BaseFileObject) javaElement.sourcefile;
InputStreamReader openReader = (InputStreamReader) javaFileObject.openReader(false);
//读文件
ArrayList<String> fileContent = getFileContent(openReader);
}
下面是读文件,普通操作,没什么好说的
/**
* <br>读取 原始文件内容
*
* @param openReader
* @return
* @author YellowTail
* @since 2019-02-21
*/
private ArrayList<String> getFileContent(InputStreamReader openReader) {
// openInputStream.re
ArrayList<String> arrayList = new ArrayList<>();
BufferedReader bReader = null;
try {
bReader = new BufferedReader(openReader);
String str;
// 按行读取字符串
while ((str = bReader.readLine()) != null) {
arrayList.add(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return arrayList;
}
替换和写文件就不讲了
2.4 lombok
lombok
是修改语法树,太厉害而导致源码看不懂。。。
略过
网友评论