Java8,如何在visitMethodInvocation中发现类和方法名?

对于Java7和Java8,我想在调用某些方法时生成警告。 如果在用户编译时存在特定的jar,则将打印该警告。

我编写了一个Annotation Processor并捕获了visitMethodInvocation()。 现在,我想要提取类和方法名称将被调用。

有可能吗? 或者如何处理这个?

你可以这样做:

 package mystuff; import com.sun.source.tree.*; import com.sun.source.util.*; import java.util.*; import javax.annotation.processing.*; import javax.lang.model.element.*; import javax.tools.*; @SupportedAnnotationTypes("*") public class Proc extends AbstractProcessor{ @Override public boolean process(Setannotations,RoundEnvironment roundEnvironment){ final Trees trees=Trees.instance(processingEnv); for(Element element:roundEnvironment.getRootElements()){ TreePath path=trees.getPath(element); final CompilationUnitTree compilationUnit=path.getCompilationUnit(); compilationUnit.accept(new TreeScanner(){ @Override public Object visitMethodInvocation(MethodInvocationTree tree,Object data){ tree.getMethodSelect().accept(new SimpleTreeVisitor(){ @Override public Object visitMemberSelect(MemberSelectTree tree,Object data){ processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,String.format("class: %1$s\nmethod: %2$s",tree.getExpression(),tree.getIdentifier())); return null; } },null); return null; } },null); } return true; } } 

我使用该处理器来处理下面的类

 package stuff; import java.util.*; @MyAnnotation class MyProgram{ public void run(){ System.out.println("Hello World!"); } } 

并取得了这个成果:

 class: System.out method: println 

我很确定生成的方法名称是您正在寻找的。 我很确定“课程”并不完全符合您的要求,但这是一个非常好的开始。

在我的例子中,你可能希望它为类打印“java.io.PrintStream”。 为此,您可以使用processingEnv.getElementUtils().getTypeElement("java.lang.System")来获取表示系统类的TypeElement。 然后,您可以使用processingEnv.getElementUtils().getAllMembers()来获取系统类的每个成员。 通过这个来找出答案。 使用asType方法获取其类型。

前一段是粗略的简化。 处理器不知道out是一个类的静态成员,它是隐式导入的java.lang包的一部分。 所以你的代码将不得不尝试找不到以下类Systemjava.util.System (因为它在import中), System.outjava.util.System.outjava.lang.System.out

我只处理了MemberSelect。 您将不得不处理其他可能性,包括MethodInvocation。 例如new Object().toString().hashCode()应该是class = Object,method = hashCode。

作为@emory的优秀答案的替代方案,您可以考虑使用Checker Framework提供的可插入类型检查注释处理。 它的优点是它可以帮助您轻松确定方法调用程序的类型。 这是一个基于checker框架的示例处理器(在编译时将checker.jar添加到类路径)。

 @SupportedAnnotationTypes("*") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class MyTypeProcessor extends AbstractTypeProcessor { class MyTreePathScanner extends TreePathScanner { private final Trees trees; private final TreePath root; public MyTreePathScanner(TreePath root) { this.trees = Trees.instance(processingEnv); this.root = root; } @Override public Void visitMemberSelect(MemberSelectTree node, Void aVoid) { ExpressionTree expression = node.getExpression(); TreePath expr = TreePath.getPath(root, expression); TypeMirror type = trees.getTypeMirror(expr); Element typeElement = processingEnv.getTypeUtils().asElement(type); Optional invoker = typeElement.getEnclosedElements().stream().filter( e -> e.getSimpleName().equals(node.getIdentifier())).findFirst(); if (invoker.isPresent() && invoker.get().getKind() == ElementKind.METHOD) { System.out.println("Type: " + typeElement + ", method: " + invoker.get()); } return super.visitMemberSelect(node, aVoid); } } @Override public void typeProcess(TypeElement typeElement, TreePath root) { new MyTreePathScanner(root).scan(root, null); } } 

这是处理以下输入源。

 public class Test { public void foo() { } public static void main(String[] args) { System.out.println("Hello world!"); Test t = new Test(); t.foo(); } } 

这是输出:

 Type: java.io.PrintStream, method: println() Type: Test, method: foo()