手把手实现一个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# | 规格或技术 |
实时规范为Java(RTSJ规范)1.0 | |
Java API的XML处理(JAXP)1.0 | |
OSGI的开放服务网关规范 | |
次郎(联邦管理体系规范)1.0 | |
Java数据对象(JDO的)1.0 | |
改进的BigDecimal(Java平台,标准版#java.math) | |
加入到Java编程语言(如J2SE 5.0的泛型类型) | |
Java EE连接器架构(JCA)的1.0 | |
企业JavaBeans(EJB)2.0 | |
JAIN SLEE API规范(JSLEE)的1.0 | |
用于XML绑定的Java体系结构(JAXB)的1.0 | |
JAIN SIP API规范(JSIP)的1.0,1.1和1.2的Java ME | |
连接设备配置(CDC)的1.0为Java ME | |
移动信息设备描述(MIDP)1.0为Java ME | |
Java元数据接口(JMI)1.0 | |
一个简单的断言基金(J2SE 1.4中) | |
日志 API规范(J2SE 1.4中) | |
WBEM服务规范(J2SE 1.4中) | |
新的I / O API的Java平台(J2SE 1.4的)(妞妞) | |
JavaServer Pages标准标记库(JSTL)的1.0和1.1 [ 3 ] | |
的Java Servlet 2.3和JavaServer页面(JSP)的1.2规格 | |
Java数据库连接(JDBC)3.0 | |
Java网络启动协议和API(JNLP的),1.0,1.5和6.0 [ 4 ](Java Web Start的) | |
Java 2平台企业版(J2EE)的1.3 | |
Java 2平台标准版(J2SE)的1.4(梅林) | |
用于XML处理的Java API(JAXP)1.1和1.2 [ 5 ] | |
Java平台Micro版(Java ME)的1.0 | |
Java数据挖掘 API(JDM)1.0 | |
的Java 的USB API | |
Java EE的应用程序部署 | |
用于XML注册的Java API(JAXR)1.0 | |
Java的文档对象模型(JDOM的)1.0 | |
Java API的WSDL(WSDL4J)1.0 | |
Java EE连接器架构(JCA)的1.5 | |
的Java Speech API的2(JSAPI2) | |
Java数据库连接(JDBC)的RowSet实现 | |
的SIP Servlet API 1.0 | |
移动信息设备描述(MIDP)2.0为Java ME | |
无线消息API(WMA)的 | |
的JavaServer Faces(JSF)的1.0和1.1 [ 6 ] | |
Java内存模型和主题规范修订 | |
Java ME的Java移动媒体API(MMAPI)的 | |
有限连接设备配置(CLDC)1.1为Java ME | |
服务定位协议 “(SLP)的Java API | |
会话描述协议(SDP)的API为Java | |
Java 2平台企业版(J2EE)的1.4 | |
JavaServer页面(JSP)的2.0 | |
企业JavaBeans(EJB)2.1 | |
的Java Servlet 2.4和2.5规格[ 7 ] | |
Java管理扩展(JMX)的远程API 1.0 | |
并发实用程序(J2SE 5.0中 | |
Portlet规范 1.0 | |
内容库的Java API(JCR)的1.0 | |
使用StAX(XML的流式API) | |
Java 2平台标准版(J2SE)的5.0(虎) | |
J2ME(SATSA的安全和信任服务API) | |
位置API为Java ME 1.0 | |
用于Java平台的Web服务元数据 | |
移动3D图形API为Java ME 1.0和1.1 | |
无线行业Java技术(JTWI的) | |
一个标准扩展API 的集成开发环境 | |
Java编译器 API | |
Java类文件规范更新 | |
更多新的I / O API的Java平台(NIO2) | |
Unicode增补字符支持(增加了J2SE 5.0的支持Unicode的 3.1) | |
无线消息API 2.0 “(WMA)2.0 | |
用于XML处理的Java API(JAXP)1.3 | |
Java业务集成(JBI)的1.0 | |
Java社区进程(JCP)2.6 | |
连接设备配置(CDC)的1.1为Java ME | |
企业JavaBeans(EJB)3.0 | |
Java数据库连接(JDBC)4.0 | |
Java SE 6中Java平台的脚本 | |
XML Web服务的Java API(JAX-WS的),继承的JAX-RPC | |
的XQuery API为Java(XQJ的) | |
支付API(PAPI的) | |
高级多媒体补充 API为Java ME | |
服务数据对象(SDO), | |
OpenGL ES的Java绑定 | |
JAIN SLEE API规范(JSLEE)的1.1 | |
Java数据对象(JDO的)2.0 | |
的Java平台企业版(Java EE)的5 | |
JavaServer页面(JSP)的2.1 | |
Java数据挖掘 API(JDM)2.0 | |
移动服务架构 | |
移动服务架构2 | |
的JavaServer Faces(JSF)的1.2 | |
移动电话服务API(MTA), | |
Java管理扩展(JMX)2.0 | |
Javadoc的标签技术更新 | |
Java平台标准版(Java SE)的6(野马) | |
移动信息设备描述(MIDP)3.0为Java ME | |
BeanShell的脚本语言 | |
单位规范(见计量单位) | |
设计时元数据的的JavaServer面临的组件 | |
为Java实时规范(RTSJ规范)1.1 | |
内容库的Java API(JCR)的2.0 | |
Portlet规范 2.0 | |
的SIP Servlet API 1.1 | |
Java语言与XML用户界面标记集成(XML用户界面) | |
针对Java SE动态组件的支持(见的OSGi) | |
位置API为Java ME 2.0 | |
在Java编程语言的改进模块化支持 | |
Swing应用程序框架(Java SE 7中) | |
Java的上下文和依赖注入(焊接) | |
JSF Portlet的桥梁 | |
移动网络和移动数据API(截至7月正式计划,20日,2007年,但官方发布2。问:2008 | |
注解的Java类型(Java SE的8) | |
RESTful Web服务的Java API(JAX-RS的)1.0和1.1 | |
的JavaServer Faces(JSF)的2.0 | |
的Java平台企业版(Java EE)的6 | |
Java持久性API(JPA)的2.0 | |
Java EE连接器架构(JCA)的1.6 | |
Java消息服务 2.0(JMS) | |
Java语言规范,第三版(JLS的)(J2SE 5.0的集成的JSR 14,41,133,175,201和204) | |
Java事务API(JTA),1.0和1.1 | |
Java 3D的 API 1.3 | |
Java社区进程(JCP)的2.0,2.1和2.5。[ 8 ] | |
Java消息服务(JMS)API的1.0和1.1 | |
的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 修改语法
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>