手把手实现一个lombok

  • 一、lombok原理 JSR269
  • 二、实现步骤
  • 1.工程与环境依赖
  • 注意细节
  • 2.注解处理器
  • 3.注解
  • 4.jcTree 修改语法
  • 4.新建模块依赖我们这个jar包进行编译
  • 5.源码调试


一、lombok原理 JSR269

什么是JSR ?

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

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

的JSR#

规格或技术

1

实时规范为Java(RTSJ规范)1.0

3

Java管理扩展(JMX)的1.0,1.1和1.2 [ 2 ]

5

Java API的XML处理(JAXP)1.0

8

OSGI的开放服务网关规范

9

次郎(联邦管理体系规范)1.0

12

Java数据对象(JDO的)1.0

13

改进的BigDecimal(Java平台,标准版#java.math

14

加入到Java编程语言(如J2SE 5.0的泛型类型

16

Java EE连接器架构(JCA)的1.0

19

企业JavaBeans(EJB)2.0

22

JAIN SLEE API规范(JSLEE)的1.0

30

连接有限设备配置(CLDC)1.0 的Java ME

31

用于XML绑定的Java体系结构(JAXB)的1.0

32

JAIN SIP API规范(JSIP)的1.0,1.1和1.2的Java ME

36

连接设备配置(CDC)的1.0为Java ME

37

移动信息设备描述(MIDP)1.0为Java ME

40

Java元数据接口(JMI)1.0

41

一个简单的断言基金(J2SE 1.4中)

47

日志 API规范(J2SE 1.4中)

48

WBEM服务规范(J2SE 1.4中)

51

新的I / O API的Java平台(J2SE 1.4的)(妞妞)

52

JavaServer Pages标准标记库(JSTL)的1.0和1.1 [ 3 ]

53

的Java Servlet 2.3和JavaServer页面(JSP)的1.2规格

54

Java数据库连接(JDBC)3.0

56

Java网络启动协议和API(JNLP的),1.0,1.5和6.0 [ 4 ]Java Web Start的

58

Java 2平台企业版(J2EE)的1.3

59

Java 2平台标准版(J2SE)的1.4(梅林)

63

用于XML处理的Java API(JAXP)1.1和1.2 [ 5 ]

68

Java平台Micro版(Java ME)的1.0

73

Java数据挖掘 API(JDM)1.0

75

J2ME平台的PDA可选包

80

的Java 的USB API

82

蓝牙的Java API

88

Java EE的应用程序部署

93

用于XML注册的Java API(JAXR)1.0

94

Java规则引擎API

102

Java的文档对象模型(JDOM的)1.0

110

Java API的WSDL(WSDL4J)1.0

112

Java EE连接器架构(JCA)的1.5

113

的Java Speech API的2(JSAPI2)

114

Java数据库连接(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 ]

133

Java内存模型和主题规范修订

135

Java ME的Java移动媒体API(MMAPI)的

139

有限连接设备配置(CLDC)1.1为Java ME

140

服务定位协议 “(SLP)的Java API

141

会话描述协议(SDP)的API为Java

151

Java 2平台企业版(J2EE)的1.4

152

JavaServer页面(JSP)的2.0

153

企业JavaBeans(EJB)2.1

154

的Java Servlet 2.4和2.5规格[ 7 ]

160

Java管理扩展(JMX)的远程API 1.0

166

并发实用程序(J2SE 5.0中的java.util.concurrentjava.util.concurrent.atomicjava.util.concurrent.locks

168

Portlet规范 1.0

170

内容库的Java API(JCR)的1.0

172

Java ME的Web服务规范

173

使用StAX(XML的流式API)

175

一个Java编程语言的元数据工具

176

Java 2平台标准版(J2SE)的5.0(虎)

177

J2ME(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 的集成开发环境

199

Java编译器 API

201

扩展Java编程语言的枚举,自动装箱,静态导入循环和增强(J2SE 5.0的)

202

Java类文件规范更新

203

更多新的I / O API的Java平台(NIO2)

204

Unicode增补字符支持(增加了J2SE 5.0的支持Unicode的 3.1)

205

无线消息API 2.0 “(WMA)2.0

206

用于XML处理的Java API(JAXP)1.3

208

Java业务集成(JBI)的1.0

215

Java社区进程(JCP)2.6

218

连接设备配置(CDC)的1.1为Java ME

220

企业JavaBeans(EJB)3.0

221

Java数据库连接(JDBC)4.0

222

用于XML绑定(JAXB)的2.0 Java体系结构

223

Java SE 6中Java平台的脚本

224

XML 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),

239

OpenGL ES的Java绑定

240

JAIN SLEE API规范(JSLEE)的1.1

241

Groovy编程语言

243

Java数据对象(JDO的)2.0

244

的Java平台企业版(Java EE)的5

880

JavaServer页面(JSP)的2.1

247

Java数据挖掘 API(JDM)2.0

248

移动服务架构

249

移动服务架构2

250

常见的注解的Java平台(Java元数据设施

252

的JavaServer Faces(JSF)的1.2

253

移动电话服务API(MTA),

255

Java管理扩展(JMX)2.0

256

移动传感器API

257

非接触式通信APINFC技术

260

Javadoc的标签技术更新

269

可插拔注解处理API(Java元数据设施

270

Java平台标准版(Java SE)的6(野马)

271

移动信息设备描述(MIDP)3.0为Java ME

274

BeanShell的脚本语言

275

单位规范(见计量单位

276

设计时元数据的JavaServer面临的组件

277

Java模块系统

280

对于Java ME的XML API

281

IMS的服务API(见的IMS

282

为Java实时规范(RTSJ规范)1.1

283

内容库的Java API(JCR)的2.0

286

Portlet规范 2.0

289

的SIP Servlet API 1.1

290

Java语言与XML用户界面标记集成(XML用户界面)

291

针对Java SE动态组件的支持(见的OSGi

292

JavaTM平台上支持动态类型语言

293

位置API为Java ME 2.0

294

在Java编程语言的改进模块化支持

296

Swing应用程序框架(Java SE 7中)

299

Java的上下文和依赖注入(焊接)

301

JSF Portlet的桥梁

303

Bean验证

307

移动网络和移动数据API(截至7月正式计划,20日,2007年,但官方发布2。问:2008

308

注解的Java类型(Java SE的8)

311

RESTful Web服务的Java API(JAX-RS的)1.0和1.1

314

的JavaServer Faces(JSF)的2.0

316

的Java平台企业版(Java EE)的6

317

Java持久性API(JPA)的2.0

322

Java EE连接器架构(JCA)的1.6

325

IMS通信促成(ICE)的(见的IMS

330

对Java的依赖注入

343

Java消息服务 2.0(JMS)

354

Java的货币及货币的API

901

Java语言规范,第三版(JLS的)(J2SE 5.0的集成的JSR 14,41,133,175,201和204)

907

Java事务API(JTA),1.0和1.1

912

Java 3D的 API 1.3

913

Java社区进程(JCP)的2.0,2.1和2.5。[ 8 ]

914

Java消息服务(JMS)API的1.0和1.1

924

第二版(JVM)Java虚拟机规范(J2SE 5.0的)。[ 9 ]

926

的Java 3D API 1.5

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

手把手实现一个lombok_java

二、实现步骤

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 修改语法


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 {
}

编译后到效果

手把手实现一个lombok_jvm_02

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>