使用Java Reflection在运行时上下文中获取所有已定义的变量(内部的voodoo)

时间:2014-07-29 15:36:24

标签: java reflection

我有这段代码:

package teste;

public class Teste {

    public static void main(String[] args) {
        String arroz = "abc";
        Integer banana = 123;

        vaiBuscar();
    }

    private static void vaiBuscar() {
        Object[] obj = theMagicMethod();
        System.out.println(((String) obj[0])); 
        System.out.println(((Integer) obj[1])); 
    }
}

输出应为:

abc
123

现在有趣的部分是我可以定义的唯一方法是theMagicMethod()。 所以我需要去获取调用我的上层方法中定义的所有变量值。如果有办法做到这一点,可能是通过反思。

总结:如果除了定义方法theMagicMethod()之外你不能编辑代码的任何部分,那么这个方法应该如何使得这个程序的输出是我上面写的那个?

谢谢!

编辑:这并不需要反思才能解决。任何方式都适合,它只需要工作。

3 个答案:

答案 0 :(得分:1)

这是不可能的。反射不会让你看看方法的内部实现。

Java中没有办法查看堆栈并提取堆栈方法堆栈帧中声明的变量或任何值。

答案 1 :(得分:1)

在方法中,变量名称将丢失。

Java是基于堆栈的机器,因此看起来像

的方法
public int add(int first, int second) {
  int sum = first + second;
  return sum;
}

将丢失字节码中的sum的命名,大致读为

  pushInt firstParameter
  pushInt secondParameter
  addInt
  returnInt

请注意,中间变量名sum在编译过程中被完全销毁,因此无法从运行时检索它。

但是,还有许多其他项必须通过名称引用,因此它们的名称不会在运行时被销毁。其中一些包括

 names of classes
 names of members
 names of methods
 names of enums
 names of interfaces

因此可以在.java文件中获取所有已使用名称的子集;但是,这样的子集不会包含任何代码块的内部名称。

现在,如果使用一些调试选项集(例如-g)进行编译,则可以改进可访问的名称数量;但是,无法保证您读入的任何类都是使用调试标志集编译的(实际上,大多数类都不是编译时调试标志设置为提高加载性能)。

如果您无法为任何类执行此操作,则无法为JVM执行此操作。

现在,如果您想尝试获取未在JVM中销毁的所有信息,您可以使用JDWP(调试线协议)并查看暴露的内容;但是,我怀疑它是否能够达到“一切”,因为它只能读取“由类加载器加载的所有内容”和(记住上述语句)只能在编译时幸存下来。

答案 2 :(得分:1)

在生产代码中使用这样的东西很可能是个坏主意。

那就是说,我必须承认我一直在寻找一种方法来检查堆栈跟踪上的变量:)。这可能是一个愚蠢的想法,但它也是一个有趣的想法。

完全无法完全。确实,字节代码似乎是以不可能的方式编译的,但每个调试器都设法完成它。但是通过反射肯定是不可能的,并且在没有肮脏的黑客攻击的情况下也无法访问调用者方法的变量。如果可能的话,你可以逃避变量的范围,并且你会破坏垃圾收集器的那一天。

在任何情况下,变量名都会丢失(这就是为什么Eclipse的调试器通常只显示参数" arg1"," arg2"等等)。 / p>

您可以尝试编写java代理并使用instrumentation API。要开始使用,您可以查看以下文章:http://blog.javabenchmark.org/2013/05/java-instrumentation-tutorial.html。 Julien Poaletti并没有尝试读变量,但我想你可以建立他的榜样。

但同样,使用自定义代理并不是运营部门应该允许的。使用风险自负!