美文网首页
Annotation Processor(二) 实现自己的注解处

Annotation Processor(二) 实现自己的注解处

作者: Yellowtail | 来源:发表于2019-03-04 10:27 被阅读0次

概述

因项目需要,本人最近实现了一个自定义的注解处理器
在这里把这个过程中的一些经验分享出来

如何配置请看上一篇博客

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,分别是 JavaEclipse
在注解处理器被运行的时候,去修改这个语法树,再输出到原文件

这里有个博客有一些介绍

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 是修改语法树,太厉害而导致源码看不懂。。。
略过

相关文章

网友评论

      本文标题:Annotation Processor(二) 实现自己的注解处

      本文链接:https://www.haomeiwen.com/subject/jflbuqtx.html