序言
该篇主于实战一个简单的 processor,代码备注很详细,如果对运行和语法还有疑问的请先查阅下前面几篇,重复内容此处就不赘述了~
annotation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface JackLog {
}
processor
import com.jack.annotation.JackLog;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.model.JavacElements;
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.Name;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;
/**
* @author jack
*/
@SupportedAnnotationTypes("com.jack.annotation.JackLog")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class JackLogProcessor extends AbstractProcessor {
/**
* 抽象语法树对象
*/
private JavacTrees trees;
/**
* 用于创建语法树节点的所有方法
*/
private TreeMaker treeMaker;
/**
* 用于创建标识符
*/
private Names names;
private JavacElements elementUtils;
@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);
elementUtils = (JavacElements) processingEnv.getElementUtils();
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(JackLog.class);
set.forEach(element -> {
JCTree jcTree = trees.getTree(element);
jcTree.accept(new TreeTranslator() {
@Override
public void visitMethodDef(JCTree.JCMethodDecl jcMethodDecl) {
doOperationMethod(jcMethodDecl);
super.visitMethodDef(jcMethodDecl);
}
});
});
return true;
}
private void doOperationMethod(JCTree.JCMethodDecl jcMethodDecl) {
treeMaker.pos = jcMethodDecl.pos;
jcMethodDecl.body = treeMaker.Block(0, List.of(
getSentenceOne(),
jcMethodDecl.body,
getSentenceTwo()
));
}
private JCTree.JCStatement getSentenceOne() {
Name systemName = elementUtils.getName("System");
Name currentTimeMillisName = elementUtils.getName("currentTimeMillis");
Name longName = elementUtils.getName("Long");
// 拼接 System.currentTimeMillis()
JCTree.JCFieldAccess s1 = treeMaker.Select(treeMaker.Ident(systemName),
currentTimeMillisName);
treeMaker.Exec(treeMaker.Apply(List.nil(), s1, List.nil()));
// java.lang.Long fStart = System.currentTimeMillis()
return treeMaker.VarDef(treeMaker.Modifiers(0),
names.fromString("fStart"), treeMaker.Ident(longName),
treeMaker.Apply(List.nil(), s1, List.nil()));
}
private JCTree.JCStatement getSentenceTwo() {
Name systemName = elementUtils.getName("System");
Name currentTimeMillisName = elementUtils.getName("currentTimeMillis");
Name outName = elementUtils.getName("out");
Name printlnName = elementUtils.getName("println");
// 拼接 System.out
JCTree.JCFieldAccess s1 = treeMaker.Select(treeMaker.Ident(systemName), outName);
// 拼接 System.out.println
JCTree.JCFieldAccess s2 = treeMaker.Select(s1, printlnName);
// 拼接 System.currentTimeMillis()
JCTree.JCFieldAccess s3 = treeMaker.Select(treeMaker.Ident(systemName),
currentTimeMillisName);
// 拼接 System.currentTimeMills() - fStart
JCTree.JCBinary s4 = treeMaker.Binary(JCTree.Tag.MINUS,
treeMaker.Apply(List.nil(), s3, List.nil()),
treeMaker.Ident(names.fromString("fStart")));
JCTree.JCBinary s5 = treeMaker.Binary(JCTree.Tag.PLUS,
treeMaker.Literal("耗时:"), s4);
JCTree.JCBinary s6 = treeMaker.Binary(JCTree.Tag.PLUS, s5,
treeMaker.Literal("ms"));
// System.out.println("耗时:" + (System.currentTimeMills() - fStart) + "ms)
return treeMaker.Exec(treeMaker.Apply(List.nil(), s2, List.of(s6)));
}
}
配置文件
javax.annotation.processing.Processor
com.jack.processor.JackLogProcessor
…
结语
第一个自定义 processor 的实战,后续逐步进阶~
如果您看到了这里,欢迎和我沟通交流!
一个95后码农
个人博客:fy-blog