一、什么是lombok
lombok是一个帮助我们简化代码的jar包,在idea中设置annotation processor保证二次编译的开启,下载安装lombok的插件,不然系统会认为你的代码在无中生有报错的?
lombok常见指令,@Data @Slf4j 等,这里就不一一列举了,网上有很多,请自己查阅,我们这里主要记录lombok的一个简单实现和原理。
二、简单实现
1.创建一个maven项目,并导入需要的jar包
<dependencies>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc4</version>
</dependency>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
auto-service可以帮助我们执行processor,com.sun.tools是jdk本地的一个jar包必须加入此依赖才可以访问,或者是使用openjdk。
2.创建一个TestAnnotation注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface TestAnnotation {
}
这里值得说一下的是RetentionPolicy类型SOURCE表示在编译期运行。
3.然后就是核心的Processor
import com.google.auto.service.AutoService;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
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.ListBuffer;
import com.sun.tools.javac.util.Names;
import org.eclipse.jdt.core.dom.*;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;
/**
* Created with IntelliJ IDEA
* Description:
* JSR269
*
* @author:duanshuangquan
* @date:2019/3/11 12:07
*/
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"*"})
public class TestProcessor extends AbstractProcessor {
private Messager messager;
private JavacTrees trees;
private TreeMaker treeMaker;
private Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
System.out.println("**************** init ************* " );
super.init(processingEnv);
this.messager = processingEnv.getMessager();
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> annotation, RoundEnvironment roundEnv) {
System.out.println("************ process ************************");
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(TestAnnotation.class);
for (Element element : set) {
// 根据element的到tree
JCTree jcTree = trees.getTree(element);
jcTree.accept(new TreeTranslator(){
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
// 增加一个字段
JCTree.JCVariableDecl jcVariableDecl = treeMaker.VarDef(treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC),
getNameFromString("log"),memberAccess("java.lang.String"),null);
// 惊天大坑 才发现 。。。和string一样并不操作自身要进行赋值
jcClassDecl.defs = jcClassDecl.defs.prepend(jcVariableDecl);
super.visitClassDef(jcClassDecl);
}
});
}
return true;
}
private com.sun.tools.javac.util.Name getNameFromString(String s) {
return names.fromString(s);
}
private JCTree.JCExpression memberAccess(String components) {
String[] componentArray = components.split("\\.");
JCTree.JCExpression expr = treeMaker.Ident(getNameFromString(componentArray[0]));
for (int i = 1; i < componentArray.length; i++) {
expr = treeMaker.Select(expr, getNameFromString(componentArray[i]));
}
return expr;
}
}
在编译的时候会执行,通过println可以了解到会先执行init方法再执行processor方法,自上而下的看首先是@AutoServer注解,来自于google的auto-service包其作用是在编译的时候会生成下面的一个文件结构,根据jsr269可以了解会根据此配置来执行Processor
@SupportedSourceVersion 是声明java版本的注解
@SupportedAnnotationTypes 用来声明注解,可以是类全名的数组也可以是通配符 *
接下在是init中初始化的参数
Messager 是用来避免重复打印日志的,先不进行考虑
JavacTrees 是java的ast语法树,TreeMaker是语法树的操作符,在com.sun.tools包下,一定要使用上下文唯一的一个。
Names 这里只是为了使用他的一个方法将string转为Name。
4.在其他项目中引入此jar包。并在类上加入TestAnnotation注解,然后编译,观察class会有不一样。
5.注意代码中的惊天大坑 他竟然new。。。