Lombok实现方式JSR-269

前言 简介

Lombok是一款好用顺手的工具,就像Google Guava一样,在此予以强烈推荐,每一个Java工程师都应该使用它。Lombok是一种Java?实用工具,可用来帮助开发人员消除Java的冗长代码,尤其是对于简单的Java对象(POJO)。它通过注释实现这一目的。通过在开发环境中实现Lombok,开发人员可以节省构建诸如hashCode()和equals()这样的方法以及以往用来分类各种accessor和mutator的大量时间。
Lombok的实现方式是什么呢?
新建一个测试类使用Lombok的Getter和Setter注解,通过IDEA进行编译

import lombok.Getter; import lombok.Setter; @Getter@Setterpublic class UserInfo {private String userId; private String userName; }

打开编译后生成的UserInfo.class文件
Lombok实现方式JSR-269
文章图片

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

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

从类名可以猜到是什么节点使用的这里挑几个常用的解释
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; @Overridepublic 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); }@Overridepublic boolean process(Set annotations, RoundEnvironment roundEnv) {//获取注解标识的类Set set = roundEnv.getElementsAnnotatedWith(getAnnotation()); //拿到语法树set.stream().map(element -> trees.getTree(element)).forEach(jcTree -> jcTree.accept(new TreeTranslator() {//拿到类定义@Overridepublic void visitClassDef(JCTree.JCClassDecl jcClassDecl) {List 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 getAnnotation(); }

用于处理Setter注解 继承MyAbstractProcessor
@SupportedAnnotationTypes("com.ingxx.processor.Setter") //注解处理器作用于哪个注解 也可以重写getSupportedAnnotationTypes@SupportedSourceVersion(SourceVersion.RELEASE_8) //可以处理什么版本 也可以重写getSupportedSourceVersionpublic class SetterProcessor extends MyAbstractProcessor {@Overridepublic JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {ListBuffer 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)); }@Overridepublic Class 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) //可以处理什么版本 也可以重写getSupportedSourceVersionpublic class GetterProcessor extends MyAbstractProcessor {@Overridepublic JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {ListBuffer 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); }@Overridepublic Class 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@Setterpublic 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实现方式JSR-269】以上就是从Lombok到JSR-269的详细内容,更多关于JS 反射机制的资料请关注脚本之家其它相关文章!

    推荐阅读