定义注解com.google.auto.service auto-service-annotations1.0.rc7 true compile com.google.auto.service auto-service1.0.rc7 true compile com.sun tools1.8.0 true compile
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) @documented public @interface TakeTime { String tag() default ""; }实现注解处理器
import com.google.auto.service.AutoService; import com.sun.source.tree.Tree; 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.util.Context; import com.sun.tools.javac.util.List; 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.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import java.util.Set; @SupportedSourceVersion(value = SourceVersion.RELEASE_8) @SupportedAnnotationTypes(value = {"cn.kanyun.annotation_processor.taketime.TakeTime"}) @AutoService(Processor.class) public class TakeTimeProcessor extends AbstractProcessor { private Messager messager; private Filer filer; private JavacElements elementUtils; private Types typeUtils; private JavacTrees trees; private TreeMaker treeMaker; private Names names; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); messager = processingEnv.getMessager(); filer = processingEnv.getFiler(); elementUtils = (JavacElements) processingEnv.getElementUtils(); typeUtils = processingEnv.getTypeUtils(); 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) { // roundEnv.getRootElements()会返回工程中所有的Class,在实际应用中需要对各个Class先做过滤以提高效率,避免对每个Class的内容都进行扫描 roundEnv.getRootElements(); messager.printMessage(Diagnostic.Kind.NOTE, "TakeTimeProcessor注解处理器处理中"); TypeElement currentAnnotation = null; // 遍历注解集合,也即@SupportedAnnotationTypes中标注的类型 for (TypeElement annotation : annotations) { messager.printMessage(Diagnostic.Kind.NOTE, "遍历本注解处理器处理的所有注解,当前遍历到的注解是:" + annotation.getSimpleName()); currentAnnotation = annotation; } // 获取所有包含 TakeTime 注解的元素(roundEnv.getElementsAnnotatedWith(TakeTime.class))返回所有被注解了@Factory的元素的列表。你可能已经注意到,我们并没有说“所有被注解了@TakeTime的方法的列表”,因为它真的是返回Element的列表。请记住:Element可以是类、方法、变量等。所以,接下来,我们必须检查这些Element是否是一个方法) Set extends Element> elementSet = roundEnv.getElementsAnnotatedWith(TakeTime.class); messager.printMessage(Diagnostic.Kind.NOTE, "TakeTimeProcessor注解处理器处理@TakeTime注解"); for (Element element : elementSet) { //获取注解 TakeTime TakeTimeAnnotation = element.getAnnotation(TakeTime.class); //获取注解中配置的值 String tag = TakeTimeAnnotation.tag(); messager.printMessage(Diagnostic.Kind.NOTE, currentAnnotation.getSimpleName() + "注解上设置的值为:" + tag); // TypeSpec typeSpec = generateCodeByPoet(typeElement, null); // 方法名(这里之所以是方法名,是因为这个注解是标注在方法上的) String methodName = element.getSimpleName().toString(); // 类名[全限定名] // element.getEnclosingElement()返回封装此元素(非严格意义上)的最里层元素,由于我们在上面判断了element是method类型,所以直接封装method的的就是类了 // http://www.169it.com/article/3400309390285698450.html String className = element.getEnclosingElement().toString(); messager.printMessage(Diagnostic.Kind.NOTE, "当前被标注注解的方法所在的类是:" + className); messager.printMessage(Diagnostic.Kind.NOTE, currentAnnotation.getSimpleName() + "当前被标注注解的方法是:" + methodName); // JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build(); enhanceMethodDecl(elementUtils.getTree(element), tag, className + "." + methodName); if (element.getKind() == ElementKind.FIELD) { // 当前element是字段类型 VariableElement variableElement = (VariableElement) element; messager.printMessage(Diagnostic.Kind.ERROR, "字段不能使用@TakeTime注解", element); } if (element.getKind() == ElementKind.CONSTRUCTOR) { // 当前element是构造方法类型 } } return false; } private JCTree.JCMethodDecl enhanceMethodDecl(JCTree jcTree, String tag, String methodName) { JCTree.JCMethodDecl jcMethodDecl = (JCTree.JCMethodDecl) jcTree; // 生成表达式System.currentTimeMillis() JCTree.JCexpressionStatement time = treeMaker.Exec(treeMaker.Apply( //参数类型(传入方法的参数的类型) 如果是无参的不能设置为null 使用 List.nil() List.nil(), memberAccess("java.lang.System.currentTimeMillis"), //因为不需要传递参数,所以直接设置为List.nil() 不能设置为null List.nil() //参数集合[集合中每一项的类型需要跟第一个参数对照] // List.of(treeMaker.Literal()) ) ); // 编译后该方法会存在一个startTime的变量,其值为编译时的时间 JCTree.JCVariableDecl startTime = createVarDef(treeMaker.Modifiers(0), "startTime", memberAccess("java.lang.Long"), treeMaker.Literal(System.currentTimeMillis())); // 耗时计算表示式 JCTree.JCexpressionStatement timeoutStatement = treeMaker.Exec( treeMaker.Apply( List.of(memberAccess("java.lang.Long"), memberAccess("java.lang.Long")), memberAccess("java.lang.Math.subtractExact"), List.of(time.expr, treeMaker.Ident(startTime.name)) ) ); // messager.printMessage(Diagnostic.Kind.NOTE, "::::::::::::::::::::"); messager.printMessage(Diagnostic.Kind.NOTE, timeoutStatement.expr.toString()); // 生成表达式System.out.println() JCTree.JCexpressionStatement TakeTime = treeMaker.Exec(treeMaker.Apply( //参数类型(传入方法的参数的类型) 如果是无参的不能设置为null 使用 List.nil() List.of(memberAccess("java.lang.String"), memberAccess("java.lang.String"), memberAccess("java.lang.Long")), // 因为这里要传多个参数,所以此处应使用printf,而不是println memberAccess("java.lang.System.out.printf"), //取到前面定义的startTime的变量 // List.of(treeMaker.Ident(startTime.name)) // 取得结果 List.of(treeMaker.Literal(">>>>>>>>TAG:%s -> 方法%s执行用时:%d<<<<<<<"), treeMaker.Literal(tag), treeMaker.Literal(methodName), timeoutStatement.getexpression()) ) ); // catch中的代码块 JCTree.JCBlock catchBlock = treeMaker.Block(0, List.of( treeMaker.Throw( // e 这个字符是catch块中定义的变量 treeMaker.Ident(getNameFromString("e")) ) )); // finally代码块中的代码 JCTree.JCBlock finallyBlock = treeMaker.Block(0, List.of(TakeTime)); List三、创建测试工程statements = jcMethodDecl.body.getStatements(); // 遍历方法体中每一行(断句符【分号/大括号】)代码 for (JCTree.JCStatement statement : statements) { messager.printMessage(Diagnostic.Kind.NOTE, "遍历方法体中的statement:" + statement); messager.printMessage(Diagnostic.Kind.NOTE, "该statement的类型:" + statement.getKind()); if (statement.getKind() == Tree.Kind.RETURN) { messager.printMessage(Diagnostic.Kind.NOTE, "该statement是Return语句"); break; } } // jcMethodDecl.body即为方法体,利用treeMaker的Block方法获取到一个新方法体,将原来的替换掉 jcMethodDecl.body = treeMaker.Block(0, List.of( // 定义开始时间,并附上初始值 ,初始值为编译时的时间 startTime, treeMaker.Exec( // 这一步 将startTime变量进行赋值 其值 为(表达式也即运行时时间) startTime = System.currentTimeMillis() treeMaker.Assign( treeMaker.Ident(getNameFromString("startTime")), time.getexpression() ) ), // 添加TryCatch treeMaker.Try(jcMethodDecl.body, List.of(treeMaker.Catch(createVarDef(treeMaker.Modifiers(0), "e", memberAccess("java.lang.Exception"), null), catchBlock)), finallyBlock) // 下面这段是IF代码,是我想在try catch finally后添加return代码(如果有需要的话),结果发现 如果不写下面的代码的话 // Javac会进行判断,如果这个方法有返回值的话,那么Javac会自动在try块外定义一个变量,同时找到要上一个return的变量并赋值 // 然后返回,具体可以查看编译后的字节码的反编译文件,如果该方法没有返回值,那么什么也不做 // 根据返回值类型,判断是否在方法末尾添加 return 语句 判断返回类型的Kind是否等于TypeKind.VOID // treeMaker.If(treeMaker.Parens( // treeMaker.Binary( // JCTree.Tag.EQ, // treeMaker.Literal(returnType.getKind().toString()), // treeMaker.Literal(TypeKind.VOID.toString())) // ), // // //符合IF判断的Statement // treeMaker.Exec(treeMaker.Literal("返回类型是Void,不需要return")), 不符合IF判断的Statement // null // ) ) ); return jcMethodDecl; } private JCTree.JCVariableDecl createVarDef(JCTree.JCModifiers modifiers, String name, JCTree.JCexpression varType, JCTree.JCexpression init) { return treeMaker.VarDef( modifiers, //名字 getNameFromString(name), //类型 varType, //初始化语句 init ); } 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; } }
pom.xml自定义的注解处理器,主要 *** 作Javac在编译被注解标注的方法时,在生成字节码时添加自己的逻辑
首先在 方法体的开头 插入一条当前时间的变量 ,并赋值为 System.currentTimeMillis()然后将整个方法体包括在try块中,添加catch 即finally catch块中直接定义异常并跑出,finally块中打印用时语句!
源码com test1.0.0
public class Test { @TakeTime public void test() { System.out.println("Hello World"); } }编译后
public class Test { Long startTime = 1642848534783L; startTime = System.currentTimeMillis(); public void test() { try { System.out.println("Hello World!"); } catch (Exception var6) { throw var6; } fianlly { System.out.printf(">>>>>>>TAG:%s --> 方法%s执行用时: %d<<<", "", "com.huawei.com.it.czrp.Test.test", Math.subtractExact(System.currentTimeMillis(), startTime)); } } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)