Javareflection:在自定义AbstractProcessor中查找方法用法

我是反思的新手。 有没有办法检测调用特定方法的位置? 例如:

public class MyClass { public static void method(){ //DO SOMETHING } } public class Test { public test(){ MyClass.method(); } } public class MyProcessor extends AbstractProcessor { public boolean process(Set annotations, RoundEnvironment roundEnv) { Method method = MyClass.class.getDeclaredMethod("method"); Class classWhereMethodIsInvoked = obtainClassWhereMethodIsInvoked(method); } public Class obtainClassWhereMethodIsInvoked(Method method) { //here I want to search one class that invoke that method, in this case Test.class } } 

是这样的可能还是我疯了?

是的,如果你真的想要它是可行的。 您可以使用classLoader搜索类路径,并通过所有类文件扫描方法名称。 下面是一个非常简单的例子,表明它是可行的。 在下面的示例中,我找到了在此类中使用的“println”方法的用法。 基本上,您可以将范围从我的示例中的一个文件扩展到所有类文件。

 public class SearchClasses { /** * @param args the command line arguments */ public static void main(String[] args) throws FileNotFoundException { // InputStream is = SearchClasses.class.getClassLoader().getResourceAsStream("resources.SearchClasses.class"); InputStream is = new FileInputStream(new File("build/classes/resources/SearchClasses.class")); boolean found = false; Scanner scanner = new Scanner(is); while (scanner.hasNext()) { if (scanner.nextLine().contains("println")) { System.out.print("println found"); found = true; break; } } if (!found) { System.out.print("println NOT found"); } } public static void testMethod() { System.out.println("testing"); } } 

在我的IDE中,我不得不使用FileInputStream来访问我正在搜索的类文件….但是如果你正在搜索jar文件,那么你可以使用classLoader代替。 你需要机制来搜索所有的类路径…这不是不可能的,但为了简洁,我把它留给了我们。

编辑:这是尝试让它完全工作..搜索类路径中的所有文件为您的方法。

 public class SearchClasses { /** * @param args the command line arguments * @throws java.io.FileNotFoundException */ public static void main(String[] args) throws FileNotFoundException, IOException { printAllFileWithMethod("println"); } public static void printAllFileWithMethod(String methodName) throws FileNotFoundException, IOException { Enumeration roots = SearchClasses.class.getClassLoader().getResources(""); List allClassFiles = new ArrayList<>(); while (roots.hasMoreElements()) { File root = new File(roots.nextElement().getPath()); allClassFiles.addAll(getFilesInDirectoryWithSuffix(root, "class")); } for (File classFile : allClassFiles) { InputStream is = new FileInputStream(classFile); boolean found = false; Scanner scanner = new Scanner(is); while (scanner.hasNext()) { if (scanner.nextLine().contains(methodName)) { System.out.print(methodName + " found in " + classFile.getName() + "\n"); found = true; break; } } } } public static void testMethod() { System.out.println("testing"); } static List getFilesInDirectoryWithSuffix(File dir, String suffix) { List foundFiles = new ArrayList<>(); if (!dir.isDirectory()) { return foundFiles; } for (File file : dir.listFiles()) { if (file.isDirectory()) { foundFiles.addAll(getFilesInDirectoryWithSuffix(file, suffix)); } else { String name = file.getName(); if (name.endsWith(suffix)) { foundFiles.add(file); } } } return foundFiles; } } 

正如评论中所提到的,Apache BCEL适合您的问题。 这些库通常特别用于从生成的字节码确定编译时信息,例如方法使用和控制流分析,并且这些信息很难(如果不是不可能的话)使用reflection来检索。 如果使用BCEL解决方案,则可能不再需要自定义注释处理器。

但由于您似乎已经在使用自定义注释处理器,因此它的重点是能够处理源文件中的注释。 因此,一种方法是定义标记正在调用的方法的自定义注释,并让自定义处理器读取这些注释以了解哪些类调用哪些方法:

 @CallerClass("MyClass.method") public class Test { public test() { MyClass.method(); } } 

在上面的(普通)示例中,自定义CallerClass注释标记一个类调用括号内注释元素中指定的方法。 注释处理器可以读取此注释并构造调用者信息。

您可以定义自己的机制。 使用Map存储每个方法的调用者:

 public static Map> callStack = new HashMap>(); public static void registerCaller(Method m) { List callers = callStack.get(m); if (callers == null) { callers = new ArrayList(); callStack.put(m, callers); } StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); callers.add(stackTraceElements[3].getClassName()); } 

目标类:

 class MyClass { public static void method() { registerCaller(new Object(){}.getClass().getEnclosingMethod()); // DO SOMETHING } } 

一些来电课程:

 package the.package.of; class Test { public void test() { MyClass.method(); } } class Foo { public void bar() { MyClass.method(); } } 

最后,测试:

 new Test().test(); new Foo().bar(); Method method = MyClass.class.getDeclaredMethod("method"); for (String clazz : callStack.get(method)) { System.out.println(clazz); } 

印刷品:

 the.package.of.Test the.package.of.Foo 

好吧,如果您使用Eclipse作为IDE,您可以通过“Open Call Hierarchy”function找到完整的调用层次结构。 这将在任何开放的Eclipse项目中找到您的方法的所有用法。 但是,如果要在运行时以编程方式查找,则需要集成一些库,这些库可以静态分析类路径的字节码以使用您的方法。

您可以在测试方法内获取堆栈跟踪:

 public class Test { public void test() { System.out.println(getCallerClass()); } public static String getCallerClass() { for (StackTraceElement e: Thread.currentThread().getStackTrace()) { if (!"java.lang.Thread".equals(e.getClassName()) && !e.getClassName().equals(Test.class.getName())) return e.getClassName(); } return null; } }