我的编程空间,编程开发者的网络收藏夹
学习永远不晚

java冷知识:javac AbstractProcessor详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

java冷知识:javac AbstractProcessor详解

它可以做什么?

它做的事情当然是生成新类或修改原始的类,比如你遇到这样的情况下就可以使用:

  • 反射好慢,曾见过一个大厂大量是Gson,由于Gson序列化时大量使用了反射,每一个field,每一个get、set都需要用反射,由此带来了性能问题。解决方法就是使用它尽量减少反射(替换成JSONObject)
  • 生成代码,只要是有注解的地方都可以读取,总之很多(有些android orm框架)

Processor

javax.annotation.processing.Processor 这个接口将提供注解处理,它遵循SPI规约进行拓展,jdk默认就有很多处理器的实现。

AbstractProcessor

注解处理器是最重要的拓展处理类了。

注意:请确认JAVA的环境变量已经配置成功,并且把tools.jar(它源于此包)加入到自己电脑的环境变量中

ProcessingEnvironment 是一个注解处理工具的集合
Element 是一个接口,表示一个程序元素,它可以是包、类、方法或者一个变量。Element已知的子接口有:

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

源码

重点关注process方法


// 源于javax.annotation.processing;
public abstract class AbstractProcessor implements Processor {
    // 集合中指定支持的注解类型的名称(这里必须时完整的包名+类名)
    public Set<String> getSupportedAnnotationTypes() {
            SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class);
            if  (sat == null) {
                if (isInitialized())
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                             "No SupportedAnnotationTypes annotation " +
                                                             "found on " + this.getClass().getName() +
                                                             ", returning an empty set.");
                return Collections.emptySet();
            }
            else
                return arrayToSet(sat.value());
        }
    // 指定当前正在使用的Java版本
    public SourceVersion getSupportedSourceVersion() {
        SupportedSourceVersion ssv = this.getClass().getAnnotation(SupportedSourceVersion.class);
        SourceVersion sv = null;
        if (ssv == null) {
            sv = SourceVersion.RELEASE_6;
            if (isInitialized())
                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                         "No SupportedSourceVersion annotation " +
                                                         "found on " + this.getClass().getName() +
                                                         ", returning " + sv + ".");
        } else
            sv = ssv.value();
        return sv;
    }
    // 初始化处理器
    public synchronized void init(ProcessingEnvironment processingEnv) {
        if (initialized)
            throw new IllegalStateException("Cannot call init more than once.");
        Objects.requireNonNull(processingEnv, "Tool provided null ProcessingEnvironment");
 
        this.processingEnv = processingEnv;
        initialized = true;
    }
    
    public abstract boolean process(Set<? extends TypeElement> annotations,
                                    RoundEnvironment roundEnv);
}

实现一个打印可以API的功能

由于本人是maven环境,以此展开讲


<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <!--Disable annotation processing for ourselves-->
                <!--<compilerArgument>-proc:none</compilerArgument>-->
            </configuration>
        </plugin>
    </plugins>
</build>

步骤1:实现一个注解处理器


@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ApiAnnotation {
    String author() default "alex.chen";
    String date();
    int version() default 1;
}
@SupportedAnnotationTypes({"com.kxtx.annotation.ApiAnnotation"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
//@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
    //类名的前缀、后缀
    public static final String SUFFIX = "AutoGenerate";
    public static final String PREFIX = "My_";
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        Messager messager = processingEnv.getMessager();
        for (TypeElement typeElement : annotations) {
            for (Element e : env.getElementsAnnotatedWith(typeElement)) {
                //打印
                messager.printMessage(Diagnostic.Kind.WARNING, "Printing:" + e.toString());
                messager.printMessage(Diagnostic.Kind.WARNING, "Printing:" + e.getSimpleName());
                messager.printMessage(Diagnostic.Kind.WARNING, "Printing:" + e.getEnclosedElements().toString());
 
                //获取注解
                ApiAnnotation annotation = e.getAnnotation(ApiAnnotation.class);
                //获取元素名并将其首字母大写
                String name = e.getSimpleName().toString();
                char c = Character.toUpperCase(name.charAt(0));
                name = String.valueOf(c + name.substring(1));
                //包裹注解元素的元素, 也就是其父元素, 比如注解了成员变量或者成员函数, 其上层就是该类
                Element enclosingElement = e.getEnclosingElement();
                //获取父元素的全类名,用来生成报名
                String enclosingQualifiedname;
                if (enclosingElement instanceof PackageElement) {
                    enclosingQualifiedname = ((PackageElement) enclosingElement).getQualifiedName().toString();
                } else {
                    enclosingQualifiedname = ((TypeElement) enclosingElement).getQualifiedName().toString();
                }
                try {
                    //生成包名
                    String generatePackageName = enclosingQualifiedname.substring(0, enclosingQualifiedname.lastIndexOf("."));
                    // 生成的类名
                    String genarateClassName = PREFIX + enclosingElement.getSimpleName() + SUFFIX;
                    //创建Java 文件
                    JavaFileObject f = processingEnv.getFiler().createSourceFile(genarateClassName);
                    // 在控制台输出文件路径
                    messager.printMessage(Diagnostic.Kind.WARNING, "Printing: " + f.toUri());
                    Writer w = f.openWriter();
                    try {
                        PrintWriter pw = new PrintWriter(w);
                        pw.println("package " + generatePackageName + ";");
                        pw.println("\npublic class " + genarateClassName + " { ");
                        pw.println("\n    ");
                        pw.println("    public static void print" + name + "() {");
                        pw.println("        // 注解的父元素: " + enclosingElement.toString());
                        pw.println("        System.out.println(\"代码生成的路径: " + f.toUri() + "\");");
                        pw.println("        System.out.println(\"注解的元素: " + e.toString() + "\");");
                        pw.println("        System.out.println(\"注解的版本: " + annotation.version() + "\");");
                        pw.println("        System.out.println(\"注解的作者: " + annotation.author() + "\");");
                        pw.println("        System.out.println(\"注解的日期: " + annotation.date() + "\");");
 
                        pw.println("    }");
                        pw.println("}");
                        pw.flush();
                    } finally {
                        w.close();
                    }
                } catch (IOException e1) {
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                            e1.toString());
                }
            }
        }
        return true;
    }
}

步骤2:配置一个spi,在resources目录新建META-INF/services/javax.annotation.processing.Processor,内容为MyProcessor类全名。

步骤3:在另一个项目中使用@ApiAnnotation就会发现生成了一个新My_feignAutoGenerate.class文件:


public class My_feignAutoGenerate {
    public My_feignAutoGenerate() {
    }
    public static void printStartUp() {
        System.out.println("代码生成的路径: file:/C:/Users/Administrator/Desktop/feign-async-master/target/generated-sources/annotations/My_feignAutoGenerate.java");
        System.out.println("注解的元素: com.github.feign.StartUp");
        System.out.println("注解的版本: 1");
        System.out.println("注解的作者: alex");
        System.out.println("注解的日期: 2019-03-6");
    }
}

到这里基本上已经演示完了。

google的 auto-service


<dependency>
    <groupId>com.google.auto.service</groupId>
    <artifactId>auto-service</artifactId>
    <version>1.0-rc2</version>
</dependency>

这个类库非常有用,它非常简单,使用@AutoService(Processor.class)会基于该接口和注解的类上自动帮我们生成META-INF/services下对应spi文件。它实现的原理就是通过注解处理器。

javapoet

有没有觉得上面pw.println("package " + generatePackageName + ";");这样的代码很痛苦啊?

JavaPoet is a Java API for generating .java source files.


package com.example.helloworld; 
public final class HelloWorld {
  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();
 
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);

你喜欢的lombok实现原理是怎样的呢?

lombok(用来帮助开发人员消除 Java 对象 的冗长),非常好用

里面源码就不再介绍了!!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

java冷知识:javac AbstractProcessor详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

java正则知识详解

表达式意义:(推荐:java视频教程)1、字符x 字符 x。例如a表示字符a\ 反斜线字符。在书写时要写为\\。(注意:因为java在第一次解析时,把\\解析成正则表达式\,在第二次解析时再解析为,所以凡是不是1.1列举到的转义字符,包括1.1的\,而又带有的
java正则知识详解
2021-10-02

C语言冷知识之预处理字符串操作符详解

当年学习C语言的第一门课就提到过标记(Token)的概念,不过,相信在多年之后你再次听到这个术语时会一脸懵逼,比如我。因此特地翻了翻资料,整理下来这些笔记,希望对大家有所帮助
2022-11-16

Java集合HashMap的知识点详解

这篇文章主要讲解了“Java集合HashMap的知识点详解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java集合HashMap的知识点详解”吧!一、什么是哈希表在讨论哈希表之前,我们先大
2023-06-02

详解Java泛型(知识点总结)

概念来源:Java在设计开始之前不是通用的,直到它在JDK 1.5中被引入。Java泛型是通过擦除实现的。你知道什么是擦除吗?往下看。概念:常规类和方法只能使用特定类型;基本类型或自定义类。如果要编写可应用于多种类型代码的代码,这种严格的限制将对代码施加很大的
详解Java泛型(知识点总结)
2021-05-06

Java枚举类知识详细讲解

本篇内容主要讲解“Java枚举类知识详细讲解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java枚举类知识详细讲解”吧!枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常
2023-06-02

Java知识梳理之泛型用法详解

从JDK 5.0以后,Java引入了“参数化类型(Parameterized type)”的概念,允许我们在创建集合时再指定集合元素的类型。本文就来和大家深入聊聊Java泛型的使用
2022-11-13

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录