手把手实现一个lombok

本文详细介绍了如何实现一个类似于Lombok的注解处理器,涉及到JSR269规范,包括工程与环境配置,注解处理器的编写,jcTree修改语法,新建模块依赖以及源码调试等步骤,帮助读者理解注解处理器的工作原理。
摘要由CSDN通过智能技术生成

一、lombok原理 JSR269

什么是JSR ?

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。

有超过300的JSR。一些更为明显的JSRs包括:

的JSR#规格或技术
1实时规范为Java(RTSJ规范)1.0
3Java管理扩展(JMX)的1.0,1.1和1.2 [ 2 ]
5Java API的XML处理(JAXP)1.0
8OSGI的开放服务网关规范
9次郎(联邦管理体系规范)1.0
12Java数据对象(JDO的)1.0
13改进的BigDecimal(Java平台,标准版#java.math
14加入到Java编程语言(如J2SE 5.0的泛型类型
16Java EE连接器架构(JCA)的1.0
19企业JavaBeans(EJB)2.0
22JAIN SLEE API规范(JSLEE)的1.0
30连接有限设备配置(CLDC)1.0 的Java ME
31用于XML绑定的Java体系结构(JAXB)的1.0
32JAIN SIP API规范(JSIP)的1.0,1.1和1.2的Java ME
36连接设备配置(CDC)的1.0为Java ME
37移动信息设备描述(MIDP)1.0为Java ME
40Java元数据接口(JMI)1.0
41一个简单的断言基金(J2SE 1.4中)
47日志 API规范(J2SE 1.4中)
48WBEM服务规范(J2SE 1.4中)
51新的I / O API的Java平台(J2SE 1.4的)(妞妞)
52JavaServer Pages标准标记库(JSTL)的1.0和1.1 [ 3 ]
53的Java Servlet 2.3和JavaServer页面(JSP)的1.2规格
54Java数据库连接(JDBC)3.0
56Java网络启动协议和API(JNLP的),1.0,1.5和6.0 [ 4 ]Java Web Start的
58Java 2平台企业版(J2EE)的1.3
59Java 2平台标准版(J2SE)的1.4(梅林)
63用于XML处理的Java API(JAXP)1.1和1.2 [ 5 ]
68Java平台Micro版(Java ME)的1.0
73Java数据挖掘 API(JDM)1.0
75J2ME平台的PDA可选包
80的Java 的USB API
82蓝牙的Java API
88Java EE的应用程序部署
93用于XML注册的Java API(JAXR)1.0
94Java规则引擎API
102Java的文档对象模型(JDOM的)1.0
110Java API的WSDL(WSDL4J)1.0
112Java EE连接器架构(JCA)的1.5
113的Java Speech API的2(JSAPI2)
114Java数据库连接(JDBC)的RowSet实现
116的SIP Servlet API 1.0
118移动信息设备描述(MIDP)2.0为Java ME
120无线消息API(WMA)的
121应用程序隔离API
127的JavaServer Faces(JSF)的1.0和1.1 [ 6 ]
133Java内存模型和主题规范修订
135Java ME的Java移动媒体API(MMAPI)的
139有限连接设备配置(CLDC)1.1为Java ME
140服务定位协议 “(SLP)的Java API
141会话描述协议(SDP)的API为Java
151Java 2平台企业版(J2EE)的1.4
152JavaServer页面(JSP)的2.0
153企业JavaBeans(EJB)2.1
154的Java Servlet 2.4和2.5规格[ 7 ]
160Java管理扩展(JMX)的远程API 1.0
166并发实用程序(J2SE 5.0中的java.util.concurrentjava.util.concurrent.atomicjava.util.concurrent.locks
168Portlet规范 1.0
170内容库的Java API(JCR)的1.0
172Java ME的Web服务规范
173使用StAX(XML的流式API)
175一个Java编程语言的元数据工具
176Java 2平台标准版(J2SE)的5.0(虎)
177J2ME(SATSA的安全和信任服务API
179位置API为Java ME 1.0
180会话发起协议(SIP)API为Java ME
181用于Java平台的Web服务元数据
184移动3D图形API为Java ME 1.0和1.1
185无线行业Java技术(JTWI的)
187即时消息的Java MEJava SE中
198一个标准扩展API 的集成开发环境
199Java编译器 API
201扩展Java编程语言的枚举,自动装箱,静态导入循环和增强(J2SE 5.0的)
202Java类文件规范更新
203更多新的I / O API的Java平台(NIO2)
204Unicode增补字符支持(增加了J2SE 5.0的支持Unicode的 3.1)
205无线消息API 2.0 “(WMA)2.0
206用于XML处理的Java API(JAXP)1.3
208Java业务集成(JBI)的1.0
215Java社区进程(JCP)2.6
218连接设备配置(CDC)的1.1为Java ME
220企业JavaBeans(EJB)3.0
221Java数据库连接(JDBC)4.0
222用于XML绑定(JAXB)的2.0 Java体系结构
223Java SE 6中Java平台的脚本
224XML Web服务的Java API(JAX-WS的),继承的JAX-RPC
225的XQuery API为Java(XQJ的)
226可调节2D矢量图形 API 的Java ME
229支付API(PAPI的)
231针对OpenGL的Java绑定
234高级多媒体补充 API为Java ME
235服务数据对象(SDO),
239OpenGL ES的Java绑定
240JAIN SLEE API规范(JSLEE)的1.1
241Groovy编程语言
243Java数据对象(JDO的)2.0
244的Java平台企业版(Java EE)的5
880JavaServer页面(JSP)的2.1
247Java数据挖掘 API(JDM)2.0
248移动服务架构
249移动服务架构2
250常见的注解的Java平台(Java元数据设施
252的JavaServer Faces(JSF)的1.2
253移动电话服务API(MTA),
255Java管理扩展(JMX)2.0
256移动传感器API
257非接触式通信APINFC技术
260Javadoc的标签技术更新
269可插拔注解处理API(Java元数据设施
270Java平台标准版(Java SE)的6(野马)
271移动信息设备描述(MIDP)3.0为Java ME
274BeanShell的脚本语言
275单位规范(见计量单位
276设计时元数据的JavaServer面临的组件
277Java模块系统
280对于Java ME的XML API
281IMS的服务API(见的IMS
282为Java实时规范(RTSJ规范)1.1
283内容库的Java API(JCR)的2.0
286Portlet规范 2.0
289的SIP Servlet API 1.1
290Java语言与XML用户界面标记集成(XML用户界面)
291针对Java SE动态组件的支持(见的OSGi
292JavaTM平台上支持动态类型语言
293位置API为Java ME 2.0
294在Java编程语言的改进模块化支持
296Swing应用程序框架(Java SE 7中)
299Java的上下文和依赖注入(焊接)
301JSF Portlet的桥梁
303Bean验证
307移动网络和移动数据API(截至7月正式计划,20日,2007年,但官方发布2。问:2008
308注解的Java类型(Java SE的8)
311RESTful Web服务的Java API(JAX-RS的)1.0和1.1
314的JavaServer Faces(JSF)的2.0
316的Java平台企业版(Java EE)的6
317Java持久性API(JPA)的2.0
322Java EE连接器架构(JCA)的1.6
325IMS通信促成(ICE)的(见的IMS
330对Java的依赖注入
343Java消息服务 2.0(JMS)
354Java的货币及货币的API
901Java语言规范,第三版(JLS的)(J2SE 5.0的集成的JSR 14,41,133,175,201和204)
907Java事务API(JTA),1.0和1.1
912Java 3D的 API 1.3
913Java社区进程(JCP)的2.0,2.1和2.5。[ 8 ]
914Java消息服务(JMS)API的1.0和1.1
924第二版(JVM)Java虚拟机规范(J2SE 5.0的)。[ 9 ]
926的Java 3D API 1.5

JSR269 可插拔的注解处理API,其原理如下:

在这里插入图片描述

二、实现步骤

1.工程与环境依赖

  1. 配置maven 插件,pom.xml 编译参数
<build>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <encoding>UTF-8</encoding>
                    <compilerArgs>
                        <arg>-parameters</arg>
                        <arg>-proc:none</arg>
                        <arg>-XDignore.symbol.file</arg>
                    </compilerArgs>
                    <compilerArguments>
                        <bootclasspath>
                            ${java.home}/lib/rt.jar${path.separator}${java.home}/lib/jce.jar${path.separator}${java.home}/../lib/tools.jar
                        </bootclasspath>
                    </compilerArguments>
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
    </build>
注意细节
  1. Lombok项目本身要加 编译 参数 ,防止编译处理器无法实例化:-proc:none
  2. 要添加编译 类路径 bootclasspath: 指定tool.jar
  3. 在测试的时候 要构建一个新的工程,用一个新的IDEA窗口打开

2.注解处理器

  • 打印编译信息
  1. 编写注解处理器,实现AbstractProcessor
  2. 基于SPI指定处理器的路径 :工程/resources/META-INF/services/javax.annotation.processing.Processor
  3. 打印消息的时候,maven 用System.out, idea用Messager
  4. 我当时用ide 编译时一直报错 ,我没太在意使用mvn 命令处理的, mvn命令没错误

Error:java: 服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor: Provider com.wfg.HelloProcessor not found时抛出异常错误

在项目编写的目录下操作下面的命令

mvn compile  编译
mvn package 打包
mvn install 安装到本地仓库

mvn exec:java -Dexec.mainClass="hello.HelloWorld" 运行main类,此处用不到这个命令
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.wfg.MyHello")
public class HelloProcessor  extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        System.out.println("这是我的第一人编译注释处理器");
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,"这是我的处理器");
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }
}

3.注解

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

}

4.jcTree 修改语法

  • 构建一个hello world 语句

import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Names;

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


@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("org.myLombok.Hello")
public class HelloProcessor  extends AbstractProcessor {
    private JavacTrees javacTrees; // 获取 JcTree
    private TreeMaker treeMaker; // 构建生成 jcTree
    private Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        System.out.println("这是我的第一人编译注释处理器");
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,"这是我的处理器");
        javacTrees = JavacTrees.instance(processingEnv);// 语法树
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        super.init(processingEnv);
        this.names = Names.instance(context);

    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        annotations.stream()
                .flatMap(t->roundEnv.getElementsAnnotatedWith(t).stream())
                .forEach(t->{
                    JCTree tree = javacTrees.getTree(t);
                 // 基于访问者设计模式 去修改方法
                    tree.accept(new TreeTranslator(){
                        @Override
                        public void visitMethodDef(JCTree.JCMethodDecl tree) {
//                            System.out.println("hello world");
                            JCTree.JCStatement sysout = treeMaker.Exec(
                                    treeMaker.Apply(
                                            List.nil(),
                                            select("System.out.println"),
                                            List.of(treeMaker.Literal("hello world!")) // 方法中的内容
                                    )
                            );
                            // 覆盖原有的语句块
                            tree.body.stats=tree.body.stats.append(sysout);
                            super.visitMethodDef(tree);
                        }
                    });
                });

        return true;
    }


    private JCTree.JCFieldAccess select(JCTree.JCExpression selected, String expressive) {
        return treeMaker.Select(selected, names.fromString(expressive));
    }

    private JCTree.JCFieldAccess select(String expressive) {
        String[] exps = expressive.split("\\.");
        JCTree.JCFieldAccess access = treeMaker.Select(ident(exps[0]), names.fromString(exps[1]));
        int index = 2;
        while (index < exps.length) {
            access = treeMaker.Select(access, names.fromString(exps[index++]));
        }
        return access;
    }

    private JCTree.JCIdent ident(String name) {
        return treeMaker.Ident(names.fromString(name));
    }
}

4.新建模块依赖我们这个jar包进行编译

/**
 * @author wufagang
 * @description
 * @date 2023年04月18日 20:46
 */
@MyHello
public class HelloDemo {
}

编译后到效果
在这里插入图片描述

5.源码调试

测试模块引入下面的插件

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值