从Lombok到JSR-269
前言
Lombok的出现帮助开发人员在开发工程中消除了大部分冗余代码:繁琐的get、set方法甚至建造者模式。
Lombok的实现方式是什么呢?
新建一个测试类使用Lombok的Getter和Setter注解,通过IDEA进行编译
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserInfo {
private String userId;
private String userName;
}
打开编译后生成的UserInfo.class文件

发现已经生成了get、set方法,由此可以推断出Lombok是在编译期为代码进行了增强,那么在编译期进行增强是如何实现的?
编译阶段
在JDK6提出并通过了JSR-269提案,提案通过了一组被称为“插入式注解处理器”的标准API,可以提前至编译期对代码中的特定注解进行处理, 从而影响到编译器的工作过程。
对于底层的一些实现,普遍会认为实现是像虚拟机一样使用C++实现,对于Java程序员来说并不是特别友好。但是Javac编译器是使用Java实现的更容易上手。
Javac的编译过程大致分为几步
- 准备过程:初始化插入式注解处理器
- 解析与填充符号表过程 词法、语法分析构建抽象语法树(AST)
- 插入式注解处理器的注解处理过程
- 分析与字节码生成过程
语法树变动后会再次解析与填充符号表,语法树没有变动时编译器就不会再对源码字符流操作,而是基于抽象语法树

综上所述想实现Lombok的效果只需要遵守JSR-269在编译期对AST进行操作即可实现
当然不止有Lombok通过这种方式实现,例如FindBug、MapStruct等也通过这种方式实现
实现
JCTree
JCTree是AST元素的基类,想实现效果只需要添加JCTree节点即可
JCTree是一个抽象类部分实现

从类名可以猜到是什么节点使用的这里挑几个常用的解释
JCStatement 声明语法树节点
JCBlock 语法块
JCReturn:return语句语法树节点
JCClassDecl:类定义语法树节点
JCVariableDecl:字段/变量定义语法树节点
JCMethodDecl:方法定义语法树节点
JCModifiers:访问标志语法树节点
JCExpression:表达式语法树节点,常见的子类如下
JCAssign:赋值语句语法树节点
JCIdent:标识符语法树节点 例如this
TreeMaker
主要用于生成语法树节点
代码
首先需要注解类,标明作用的范围和作用的时期,两个类分别对应Lombok的Getter、Setter
@Target({ElementType.TYPE}) //加在类上的注解
@Retention(RetentionPolicy.SOURCE) //作用于编译期
public @interface Getter {
}
@Target({ElementType.TYPE}) //加在类上的注解
@Retention(RetentionPolicy.SOURCE) //作用于编译期
public @interface Setter {
}
新建抽象注解处理器需要继承AbstractProcessor,这里使用模板方法模式
public abstract class MyAbstractProcessor extends AbstractProcessor {
//语法树
protected JavacTrees trees;
//构建语法树节点
protected TreeMaker treeMaker;
//创建标识符的对象
protected Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.trees = JavacTrees.instance(processingEnv);
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
this.treeMaker = TreeMaker.instance(context);
this.names = Names.instance(context);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//获取注解标识的类
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(getAnnotation());
//拿到语法树
set.stream().map(element -> trees.getTree(element)).forEach(jcTree -> jcTree.accept(new TreeTranslator() {
//拿到类定义
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
//拿到所有成员变量
for (JCTree tree : jcClassDecl.defs) {
if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
}
jcVariableDeclList.forEach(jcVariableDecl -> {
jcClassDecl.defs = jcClassDecl.defs.prepend(makeMethodDecl(jcVariableDecl));
});
super.visitClassDef(jcClassDecl);
}
}));
return true;
}
/**
* 创建方法
* @param jcVariableDecl
* @return
*/
public abstract JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl);
/**
* 获取何种注解
* @return
*/
public abstract Class<? extends Annotation> getAnnotation();
}
用于处理Setter注解 继承MyAbstractProcessor
@SupportedAnnotationTypes("com.ingxx.processor.Setter") //注解处理器作用于哪个注解 也可以重写getSupportedAnnotationTypes
@SupportedSourceVersion(SourceVersion.RELEASE_8) //可以处理什么版本 也可以重写getSupportedSourceVersion
public class SetterProcessor extends MyAbstractProcessor {
@Override
public JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
//生成函数体 this.name = name;
statements.append(treeMaker.Exec(treeMaker.Assign(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName()),treeMaker.Ident(jcVariableDecl.getName()))));
JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
//生成方法
return treeMaker.MethodDef(
treeMaker.Modifiers(Flags.PUBLIC), //访问标志
getNewMethodName(jcVariableDecl.getName()), //名字
treeMaker.TypeIdent(TypeTag.VOID), //返回类型
List.nil(), //泛型形参列表
List.of(getParameters(jcVariableDecl)), //参数列表
List.nil(), //异常列表
body, //方法体
null //默认方法(可能是interface中的那个default)
);
}
@Override
public Class<? extends Annotation> getAnnotation() {
return Setter.class;
}
private Name getNewMethodName(Name name) {
String fieldName = name.toString();
return names.fromString("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, name.length()));
}
private JCTree.JCVariableDecl getParameters(JCTree.JCVariableDecl prototypeJCVariable) {
return treeMaker.VarDef(
treeMaker.Modifiers(Flags.PARAMETER), //访问标志
prototypeJCVariable.name, //名字
prototypeJCVariable.vartype, //类型
null //初始化语句
);
}
}
用于处理Getter注解 继承MyAbstractProcessor
@SupportedAnnotationTypes("com.ingxx.processor.Getter") //注解处理器作用于哪个注解 也可以重写getSupportedAnnotationTypes
@SupportedSourceVersion(SourceVersion.RELEASE_8) //可以处理什么版本 也可以重写getSupportedSourceVersion
public class GetterProcessor extends MyAbstractProcessor {
@Override
public JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
//生成函数体 return this.字段名
statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
//生成方法
return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), body, null);
}
@Override
public Class<? extends Annotation> getAnnotation() {
return Getter.class;
}
private Name getNewMethodName(Name name) {
String fieldName = name.toString();
return names.fromString("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, name.length()));
}
}
编译现有类
javac -cp $JAVA_HOME/lib/tools.jar *.java -d .
新建一个测试类 IDEA中由于找不到get set方法会报错,可以忽略
@Getter
@Setter
public class UserInfo {
private String userId;
private String userName;
public static void main(String[] args) {
UserInfo userInfo = new UserInfo();
userInfo.setUserId("001");
userInfo.setUserName("得物");
System.out.println("id = "+userInfo.getUserId()+" name = "+userInfo.getUserName());
}
}
接着编译
//多个处理器用逗号分隔
javac -processor com.ingxx.processor.GetterProcessor,com.ingxx.processor.SetterProcessor UserInfo.java -d .
查看编译后的文件发现已经生成了get、set方法

从Lombok到JSR-269的更多相关文章
- Lombok 简单入门
原文地址:Lombok 简单入门 博客地址:http://www.extlight.com 一.前言 Lombok 是一个 Java 库,它作为插件安装至编辑器中,其作用是通过简单注解来精简代码,以此 ...
- Spring Boot 快速入门笔记
Spirng boot笔记 简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发 ...
- Lombok 安装、入门 - 消除冗长的 java 代码(转)
前言: 逛开源社区的时候无意发现的,用了一段时间,觉得还可以,特此推荐一下. lombok 提供了简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 java 代码.特别是相对于 ...
- Java开发速度神器Lombok,Eclipse端安装使用教程
一.Lombok简介 Lombok是一个代码生成器,可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法. 使用 ...
- Lombok介绍、使用方法和总结
1 Lombok背景介绍 官方介绍如下: Project Lombok makes java a spicier language by adding 'handlers' that know how ...
- SpringBoot集成Lombok,应用+源码解析,让代码优雅起来
一.Lombok简介 (1)Lombok官网(https://projectlombok.org/)对lombok的介绍 (2)GitHub项目地址:https://github.com/rzwits ...
- Lombok插件看法浅谈
背景 最近接触的几个工程中Lombok插件出现频率比较高,趁机了解一下原理. 简要说明: 受益于JSR 269 API,程序可以在编译阶段对AST进行节点的操作,从而注入相关的功能结点,从而包含在最终 ...
- 基于IDEA工具 lombok 的使用
一.简介 Lombok 是一种 Java™ 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO).它通过注解实现这一目的. 二.lombok的添加和常用注解 ...
- lombok的简单介绍
##lombok的使用 一直在使用lombok的set和get,对其他的功能用的比较少,蓦然发现这个库好用的功能不要太多啊 有必要深入理解一番. ###lombok安装 1 需要IDE支持,不然开发的 ...
- 简化Getter 与 Setter 的插件 Lombok
参考文档:https://www.jianshu.com/p/365ea41b3573 第一步:添加依赖 <dependency> <groupId>org.projectlo ...
随机推荐
- Socket简介 与 UDP
1. Socket 简介 2. UDP 1. Socket 简介 网络编程,是指让在不同的电脑上的软件能够进行数据传递,即进程之间的通信. 本地的进程间通信(IPC) 例如有:队列.同步(互斥锁.条件 ...
- go gin框架和springboot框架WEB接口性能对比
1 简要概述 最近看起go lang,真的被go的goroutine(协程)惊艳到了,一句 go function(){#todo},即可完成一个并发的工作. 看到gin这个web框架时,突然就特别想 ...
- 缓冲区溢出分析第07课:MS06-040漏洞研究——静态分析
前言 我在之前的课程中讨论过W32Dasm这款软件中的漏洞分析与利用的方法,由于使用该软件的人群毕竟是小众群体,因此该漏洞的危害相对来说还是比较小的.但是如果漏洞出现在Windows系统中,那么情况就 ...
- Linux系统应急响应
目录 排查用户相关的信息 排查进程端口相关的信息 查找恶意程序并杀掉 斩草除根 判断入侵方式,修复漏洞 当我们被告知一台Linux服务器被黑客入侵,黑客利用该服务器进行挖矿,并且在该服务器上放置了木马 ...
- windows核心编程-第二章 Unicode
第2章U n i c o d e 随着M i c r o s o f t公司的Wi n d o w s操作系统在全世界日益广泛的流行,对于软件开发人员来说,将目标瞄准国际上的各个不同市场,已经成为一个 ...
- node-Get&POST
Node.js GET/POST请求 var http = require('http'); var url = require('url'); var util = require('util'); ...
- Learning Memory-guided Normality代码学习笔记
Learning Memory-guided Normality代码学习笔记 记忆模块核心 Memory部分的核心在于以下定义Memory类的部分. class Memory(nn.Module): ...
- ES常用查询语法
1.query string search GET /index/type/_search?q=name:zhangsan&sort=age:desc 2.query DSL(Domain S ...
- 初步了解web
------------------------1.Web应用程序的main方法在哪里------------------------Tomcat:从启动到运行首先,我们是通过执行 Tomcat 的s ...
- Dom树,什么是dom树?
相信很多初学前端的小伙伴,学了html,css,js之后,欣喜之余还有一丝小傲娇,没有想到那些大佬们口中又 提到了DOM树.你两眼一抹黑,年轻人总是要接受社会的爱(du)护(da). DOM 是 Do ...