欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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已知的子接口有:
java冷知识:javac AbstractProcessor详解
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 对象 的冗长),非常好用

java冷知识:javac AbstractProcessor详解

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

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