【问题标题】:Java: how to get arguments passed to method that called this method?Java:如何获取传递给调用此方法的方法的参数?
【发布时间】:2011-06-24 00:53:49
【问题描述】:

在java中,可以获取调用当前方法的类和方法(获取StackTrace的方法)。

我的问题是,我可以获取传递给调用此方法的方法的参数吗?

我需要这个来进行调试。

例如:

baseClass {
   initialFunc(input) {
       var modifiedInput = input + " I modified you";
       otherClass.doSomething(modifiedInput);
   }
}

otherClass {
    doSomething(input)  {
         //GET THE ARGUMENTS PASSED TO THE METHOD OF THE CLASS THAT CALLED THIS METHOD
    }
}

可以从堆栈跟踪中获取此信息,还是有其他方法?

(请注意,我需要能够在运行时执行此操作,并且不能实际更改 baseClass 的来源,这将是我的调试类的一个功能,事先不知道来源)

谢谢。

【问题讨论】:

  • 如果您附加调试器,您可以,否则 - 不。参数可能在 CPU 寄存器中(不在堆栈上)
  • 恐怕我无法为我的目的附加调试器
  • 那么你就迷路了,除了 ClassLoader 和动态增强类来捕获参数。 (但你不会问你是否知道)
  • 如果调试器可以做到,那么它一定是有可能的。 AFAIK,调试器不使用代码检测等,因为它可以附加到任何 Java 进程。不知道这是如何工作的,也许是一些 JMX?
  • @bestsss,这让我想到了一个后续问题:stackoverflow.com/questions/4945358/… - 我接受了这个问题。

标签: java debugging arguments stack-trace inflection


【解决方案1】:

我认为使用标准 Java API 是不可能的。

您可以做的是使用 AspectJ,在调用方法处放置一个切入点,保存参数,在被调用方法处放置一个切入点并传递参数。

另一个选项(稍微高级一点)是使用自定义的字节码重写类加载器,它保存原始参数,并将它们作为额外参数传递给下一个方法。这可能需要一两天的时间来实施。合适的框架是BCELASM

【讨论】:

  • AspectJ 方法会迫使我在运行前更改源代码,所以它不是一个选项。关于另一个选项,我可以制作一个 JRE 扩展来更新每个方法使用的任何类吗?如果是这样,JRE 中使用的方法/函数是什么类?
  • 这是一个类似问题的 AspectJ 示例:stackoverflow.com/a/14089562/603516
【解决方案2】:

我认为这是可能的,因为input 超出范围但尚无法用于垃圾收集,因此该值仍然存在,但不幸的是我不相信有默认的 API 方式来访问它.这可以通过为日志记录方法自定义实现的 NDC(嵌套诊断上下文)来实现。

【讨论】:

  • 形式参数被视为局部变量,其内容存储在堆栈中,因此不会被垃圾回收。
  • 根本不需要存储在堆栈上。该方法可以内联和/或参数可以在 CPU 寄存器中(这是使用“热”代码发生的情况)
  • 你确定没有办法访问吗?
  • @Tom,如果您使用调试设置运行其他代理,您可以这样做。通常你不能。
  • @bestsss,我说的是jvm的激活记录栈。
【解决方案3】:

我不确定你为什么要在 Java 中这样做?

我能想到的唯一方法是为传递的字符串创建一个自定义包装器对象,从而每次都发送对包装器的引用而不是一个新字符串。
不过,我建议不要这样做,因为它会使您的原始代码变得混乱,并且更容易出错。

使用调试器(例如 eclipse 内置的调试器)来检查您的状态是否无法解决这个问题?

【讨论】:

    【解决方案4】:

    在我的例子中,我需要获取一个参数值,该参数值已传递给某个堆栈帧中的方法,以便稍后在执行流程中使用

    我使用ThreadLocal 来存储它,当我需要它时,我可以在代码中的任何位置检索它,因为我将它声明为public static

    这是一个骨架示例

    public static final ThreadLocal<SomeType> IMPORTANT_THREAD_LOCAL_FOR_BLA = ThreadLocal.withInitial(whatever);
    
    methodWithImportantParam(SomeType importantValue){
        // save it in the static threadLocal Field
        this.IMPORTANT_THREAD_LOCAL_FOR_BLA.get()=importantValue;// code to set the value
        // continue method logic
    }
    

    以及代码中需要该值的地方

    YourClass.IMPORTANT_THREAD_LOCAL_FOR_BLA.get()
    

    但请确保您设置值然后检索它的执行流程

    希望我的回答能为这个问题增添一些有价值的东西

    【讨论】:

      【解决方案5】:

      你可以得到调用者方法的名字和它的类,但是你必须在当前方法中添加一些代码:

          public static void main(String[] args) throws Exception {
          call();
      }
      
      private static void call() {
          Exception exception = new Exception();
          for(StackTraceElement trace : exception.getStackTrace()){
              System.out.println(trace.getMethodName());
          }
      }
      

      这将打印“call”和“main”,按调用顺序(反向)的方法名称。

      【讨论】:

        【解决方案6】:

        这可以通过反射 API 实现!

        public class StackTrace {
            public static void main(String args[]) {
                StackTrace st = new StackTrace();
                st.func();
            }
            public void func() {
                OtherClass os =new OtherClass();
                os.getStackTrace(this);
            }
        }
        
        class OtherClass {
            void getStackTrace(Object obj)  {
                System.out.println(obj.getClass());
            }
        }
        

        【讨论】:

        • 这需要我附加调试器吗?
        • 不,不是,这是 JVM 在内部使用的一种技术。玩 Reflection 真的很有趣……
        • 你可以获得调用者类 - 是的,但不是参数
        • @Ujjwal,也许您可​​以通过复制我的代码示例来扩展您的答案,并使其输出传递给调用此方法的方法的参数。 (看来这是不可能的)
        • 单独使用反射是不可能的,因为不应修改原始代码(=> 你没有地方插入反射调用)。
        猜你喜欢
        • 2012-12-12
        • 1970-01-01
        • 1970-01-01
        • 2011-09-27
        • 2013-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多