繁体   English   中英

Java反射:在自定义AbstractProcessor中查找方法用法

[英]Java Reflection: Find method usage in custom 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<? extends TypeElement> 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
   }

}

是这样的事情还是我疯了?

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

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

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

    public test() {
       MyClass.method();
    }

} 

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

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

public static Map<Method, List<String>> callStack = new HashMap<Method, List<String>>();

public static void registerCaller(Method m)
{
    List<String> callers = callStack.get(m);
    if (callers == null)
    {
        callers = new ArrayList<String>();
        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”功能找到完整的调用层次结构。 这将在任何打开的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;
    }
}

是的,如果你真的想要它是可行的。 您可以使用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<URL> roots = SearchClasses.class.getClassLoader().getResources("");

        List<File> 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<File> getFilesInDirectoryWithSuffix(File dir, String suffix) {
        List<File> 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;
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM