Lombok實現(xiàn)方式JSR-269
前言
簡介
Lombok是一款好用順手的工具,就像Google Guava一樣,在此予以強烈推薦,每一個Java工程師都應(yīng)該使用它。Lombok是一種Java™實用工具,可用來幫助開發(fā)人員消除Java的冗長代碼,尤其是對于簡單的Java對象(POJO)。它通過注釋實現(xiàn)這一目的。通過在開發(fā)環(huán)境中實現(xiàn)Lombok,開發(fā)人員可以節(jié)省構(gòu)建諸如hashCode()和equals()這樣的方法以及以往用來分類各種accessor和mutator的大量時間。
Lombok的實現(xiàn)方式是什么呢?
新建一個測試類使用Lombok的Getter和Setter注解,通過IDEA進行編譯
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserInfo {
private String userId;
private String userName;
}
打開編譯后生成的UserInfo.class文件

發(fā)現(xiàn)已經(jīng)生成了get、set方法,由此可以推斷出Lombok是在編譯期為代碼進行了增強,那么在編譯期進行增強是如何實現(xiàn)的?
編譯階段
在JDK6提出并通過了JSR-269提案,提案通過了一組被稱為“插入式注解處理器”的標(biāo)準(zhǔn)API,可以提前至編譯期對代碼中的特定注解進行處理, 從而影響到編譯器的工作過程。
對于底層的一些實現(xiàn),普遍會認(rèn)為實現(xiàn)是像虛擬機一樣使用C++實現(xiàn),對于Java程序員來說并不是特別友好。但是Javac編譯器是使用Java實現(xiàn)的更容易上手。
Javac的編譯過程大致分為幾步
- 準(zhǔn)備過程:初始化插入式注解處理器
- 解析與填充符號表過程 詞法、語法分析構(gòu)建抽象語法樹(AST)
- 插入式注解處理器的注解處理過程
- 分析與字節(jié)碼生成過程
- 語法樹變動后會再次解析與填充符號表,語法樹沒有變動時編譯器就不會再對源碼字符流操作,而是基于抽象語法樹

綜上所述想實現(xiàn)Lombok的效果只需要遵守JSR-269在編譯期對AST進行操作即可實現(xiàn)
當(dāng)然不止有Lombok通過這種方式實現(xiàn),例如FindBug、MapStruct等也通過這種方式實現(xiàn)
實現(xiàn)
JCTree
JCTree是AST元素的基類,想實現(xiàn)效果只需要添加JCTree節(jié)點即可
JCTree是一個抽象類部分實現(xiàn)

從類名可以猜到是什么節(jié)點使用的這里挑幾個常用的解釋
JCStatement 聲明語法樹節(jié)點
JCBlock 語法塊
JCReturn:return語句語法樹節(jié)點
JCClassDecl:類定義語法樹節(jié)點
JCVariableDecl:字段/變量定義語法樹節(jié)點
JCMethodDecl:方法定義語法樹節(jié)點
JCModifiers:訪問標(biāo)志語法樹節(jié)點
JCExpression:表達式語法樹節(jié)點,常見的子類如下
JCAssign:賦值語句語法樹節(jié)點
JCIdent:標(biāo)識符語法樹節(jié)點 例如this
TreeMaker
主要用于生成語法樹節(jié)點
代碼
首先需要注解類,標(biāo)明作用的范圍和作用的時期,兩個類分別對應(yīng)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;
//構(gòu)建語法樹節(jié)點
protected TreeMaker treeMaker;
//創(chuàng)建標(biāo)識符的對象
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) {
//獲取注解標(biāo)識的類
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;
}
/**
* 創(chuàng)建方法
* @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<>();
//生成函數(shù)體 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), //訪問標(biāo)志
getNewMethodName(jcVariableDecl.getName()), //名字
treeMaker.TypeIdent(TypeTag.VOID), //返回類型
List.nil(), //泛型形參列表
List.of(getParameters(jcVariableDecl)), //參數(shù)列表
List.nil(), //異常列表
body, //方法體
null //默認(rèn)方法(可能是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), //訪問標(biāo)志
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<>();
//生成函數(shù)體 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()));
}
}
編譯現(xiàn)有類
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 .
查看編譯后的文件發(fā)現(xiàn)已經(jīng)生成了get、set方法

以上就是從Lombok到JSR-269的詳細內(nèi)容,更多關(guān)于JS 反射機制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript代碼輕松實現(xiàn)網(wǎng)頁內(nèi)容禁止復(fù)制(代碼簡單)
有些時候我們寫的內(nèi)容不想被別人復(fù)制,在代碼中怎么實現(xiàn)的呢?下面小編給大家介紹javascript代碼輕松實現(xiàn)網(wǎng)頁內(nèi)容禁止復(fù)制,感興趣的童鞋一起看看吧2015-10-10
JavaScript實現(xiàn)點擊復(fù)制功能具體代碼(JS訪問剪貼板相關(guān))
這篇文章主要給大家介紹了關(guān)于JavaScript實現(xiàn)點擊復(fù)制功能(JS訪問剪貼板相關(guān))的相關(guān)資料,復(fù)制功能指的是將一個文本或者圖片等資源從一個位置通過復(fù)制的方式再次拷貝到另一個位置,需要的朋友可以參考下2023-10-10
js實現(xiàn)的修改div里img標(biāo)簽的src屬性
js實現(xiàn)的修改div里img標(biāo)簽的src屬性...2007-09-09

