首页 > 编程语言 >【JDK】自定义注释处理程序插件 AbstractProcessor

【JDK】自定义注释处理程序插件 AbstractProcessor

时间:2023-10-19 09:44:32浏览次数:46  
标签:插件 自定义 JDK AbstractProcessor 编译 处理器 注解 import 生成

1  前言

最近比较好奇,Lombok、MapStruct 里的注解,会在打包的时候会给我们的字节码文件里,写入一些东西甚至生成一些转换的字节码文件,不知道人家是如何做到的,所以抽空看了看,自己写了一个小Demo,来体验下。我们的目标就是引入自己的 AbstractProcessor 的 maven依赖包,然后能给我们生成一些东西来。

参考文档:

MapStruct文档:https://mapstruct.org/documentation/reference-guide/

MapStruct源码:https://github.com/mapstruct/mapstruct

同类文档:https://www.bilibili.com/read/cv22416305/

同类文档:https://blog.csdn.net/begefefsef/article/details/126434950

2  相关知识

我们接触的注解主要分为以下两类:

  1. 运行时注解:通过反射在运行时动态处理注解的逻辑
  2. 编译时注解:通过注解处理器在编译期动态处理相关逻辑

平时我们接触的框架大部分都是运行时注解,比如:@Autowire @Resoure @Bean 等等。那么我们平时有接触过哪些编译期注解呢,@Lombok @AutoService 等等

像这些编译时注解的作用都是自动生成代码,一是为了提高编码的效率,二是避免在运行期大量使用反射,通过在编译期利用反射生成辅助类和方法以供运行时使用

注解编译期处理流程最关键的一个类就是Processor ,它是注解处理器的接口类,我们所有需要对编译期处理注解的逻辑都需要实现这个Processor接口,当然,AbstractProcessor 抽象类帮我们写好了大部分都流程,所以我们只需要实现这个抽象类就可以很方便的定义一个注解处理器

注解处理流程由多轮完成。每一轮都从编译器在源文件中搜索注解并选择适合这些注解的 注解处理器(AbstractProcessor) 开始。每个注解处理器依次在相应的源上被调用。

如果在此过程中生成了任何文件,则将以生成的文件作为输入开始另一轮。这个过程一直持续到处理阶段没有新文件生成为止。

注解处理器的处理步骤:

  • 在java编译器中构建;
  • 编译器开始执行未执行过的注解处理器;
  • 循环处理注解元素(Element),找到被该注解所修饰的类,方法,或者属性;
  • 生成对应的类,并写入文件;
  • 判断是否所有的注解处理器都已执行完毕,如果没有,继续下一个注解处理器的执行(回到步骤1)

3  实践

看完基本的概念后,我们来开始写一个自己的 AbstractProcessor,新建工程哦。我直接贴代码了哈,大家看的时候可以自己动手试试:

3.1  注解

新建个注解,来作为处理的入口,也就是我们要对哪个注解进行处理:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
}

3.2  自定义 AbstractProcessor

创建处理类,继承 AbstractProcessor:

import com.google.auto.service.AutoService;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.Set;

/**
 * @author: xjx
 * @description
 * 自定义的编译时处理器
 */
@AutoService(Processor.class)
@SupportedAnnotationTypes("process.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyCustomProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "=============我执行了");

        try {
            printFile();
        } catch (IOException e) {
        }
        return false;
    }

    private void printFile() throws IOException {
        this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "doing print");
        //通过向Filer声明自己想要创建的java文件包名,就能获取其writer
        JavaFileObject fileObject = this.processingEnv.getFiler().createSourceFile("test.mipa.testSrcFile.myClass1");
        Writer writer = fileObject.openWriter();

        //实际应用应该需要使用JavaPoet等自动代码生成功能
        String classFileStr = "package test.mipa.testSrcFile; \n" +
                "class myClass1\n{\n" +
                "private int a = 0;\n" +
                "int getValue()\n { return this.a; }\n" +
                "}";

        //直接将待生成.java文件以String形式输入writer,编译器会将这个流程产生的文件也进行生成
        writer.write(classFileStr);

        writer.flush();
        writer.close();
    }
}

3.3  部署到本地仓库

我的pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.processor</groupId>
    <artifactId>processor</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.1.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build> 
</project>

写好后,我们 install 一下把它安装到本地仓库:

3.4  其他工程引入依赖看看效果

我找了一个我自己的工程,引入:

然后我们 install 一下,看看效果:

4  小结

好啦,体验完毕哈,接下来重点其实就在 AbstractProcessor 的逻辑里,我们想要根据什么生成什么样的东西,比如 MapStruct 它本身定义了很多模板文件,渲染匹配的,抽空还可以再看看哈,暂时就到这里哈,有理解不对的地方欢迎指正哈。

标签:插件,自定义,JDK,AbstractProcessor,编译,处理器,注解,import,生成
From: https://www.cnblogs.com/kukuxjx/p/17773926.html

相关文章

  • 【问题记录】自定义注解处理程序 AbstractProcessor,就是不生效,执行没效果
    1  前言最近在看注解处理程序,也想打包的时候,生成一点自己的东西,写了一个 AbstractProcessor,奶奶的花了两个早上,一直想不明白为什么不生效:唉,仅记录哈。......
  • JDK
    卸载JDK删除Java的安装目录删除JAVA-HOME删除path下关于Java的目录java-version安装JDK百度搜索JDK8,找到下载地址同于协议下载电脑对应的版本双击安装JDK记住安装路径配置环境变量我的电脑——>右键——>属性环境变量——>JAVA_HOME配......
  • 用户控件和自定义控件
    用户控件和自定义控件的不同点在XAML中构成用户控件的样子。子控件需要暴露的依赖属性有2种用途:1.显示2.赋值3.事件。如何暴露属性?将2种需要暴露的依赖属性定义成用户控件的新增的自定义依赖属性。TextBoxText=Binding,子控件Text用户控件被赋值,文本框变,文本框变......
  • C# AVEVA 加载其他AddIn插件
    通过程序加载了其他插件[MyAmFunctionAtt(nameof(测试功能),nameof(加载其他插件))]publicvoid加载其他插件(WindowManagerwm){varaddInM=Aveva.ApplicationFramework.AddinManager.Instance;varui=newMarUi();try{varaddin=addInM......
  • 虚幻(UnrealEngine)全局事件插件
    https://github.com/bodong1987/UnrealEngine.GlobalEvents学习Unreal的练手代码,主要用途是提供一个全局级别的消息广播与消息监听,目的是解决直接引用对象带来的强依赖的问题。详情可见github首页。 ......
  • 初学Bokeh:自定义标题【12】跬步
    初学Bokeh:自定义标题【12】跬步大多数绘图都包含标题(headline)。可以通过向figure()函数传递标题参数来为图像添加标题:p=figure(title="Headlineexample")标题文本有多种样式,示例如下:frombokeh.plottingimportfigure,show#preparesomedata#定义绘图数据x......
  • 将自定义的日志直接写入到 mysql 数据库
    将日志直接写入到MySQL数据库中可能会对性能产生影响。如果需要高性能的日志记录解决方案,则可以考虑使用专门的日志记录器,例如Elasticsearch、Logstash和Kibana(ELK)堆栈。安装MySQL数据库,并创建一个新的数据库和表来存储日志数据。使用MySQL连接器来连接到MySQL数据库。创建一个包含......
  • 查看edge浏览器插件的安装位置并将插件安装到别的浏览器
    1eage浏览器插件的位置C:\Users\你的用户名\AppData\Local\Microsoft\Edge\UserData\Default\Extensions2把插件安装到其他浏览器第一种这些都是插件的文件,点开文件夹,下面的文件夹就是插件打开浏览器的扩展程序,开发者模式需要打开把文件夹拖入即可加载成功第二种......
  • jdk8的项目迁移到jdk11以上的迁移方案
    用命令:jdeprscan--release11my-application.jar这里可以用jdeprscan--version看下是什么版本,我这边是17.0.5这里先得到一个已经被jdk8编译打包好的,测试没问题的jar文件,如上面的my-application.jar然后如果要升级到jdk17,可以用上面的命令,11改成17,这样就能检测项目是否使用......
  • Burp Suite 插件开发之UI界面——以Button举例
    Guide不管是什么软件,大多是需要UI界面的。BurpSuite的插件开发也不例外,某些场景下也需要配合UI界面才能更好操作插件的功能。UI界面的开发,无法是学习各种组件的使用方法,根据它们的使用特点去编写特定的事件函数。由于组件都是前人都写好的,我们只管拿过来使用即可,主要是熟悉组......