手把手实现一个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.concurrent ,java.util.concurrent.atomic 和java.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 ME和Java 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 | 非接触式通信API(NFC技术) |
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,其原理如下:
二、实现步骤
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>
注意细节
- Lombok项目本身要加 编译 参数 ,防止编译处理器无法实例化:-proc:none
- 要添加编译 类路径 bootclasspath: 指定tool.jar
- 在测试的时候 要构建一个新的工程,用一个新的IDEA窗口打开
2.注解处理器
- 打印编译信息
- 编写注解处理器,实现
AbstractProcessor
- 基于SPI指定处理器的路径 :工程/resources/META-INF/services/javax.annotation.processing.Processor
- 打印消息的时候,maven 用System.out, idea用
Messager
类 - 我当时用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>