java冷知识:javac AbstractProcessor详解
程序员文章站
2022-03-21 16:30:12
目录它可以做什么?processorabstractprocessor源码google的 auto-servicejavapoet你喜欢的lombok实现原理是怎样的呢?它可以做什么?它做的事情当然是...
它可以做什么?
它做的事情当然是生成新类或修改原始的类,比如你遇到这样的情况下就可以使用:
- 反射好慢,曾见过一个大厂大量是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; } /** * 这些注解是否由此 processor 处理,该方法返回ture表示该注解已经被处理, 后续不会再有其他处理器处理; 返回false表示仍可被其他处理器处理 */ 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 对象 的冗长),非常好用
里面源码就不再介绍了!!
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。