记录Clojure对Java对象的所有方法调用
我正在为一些Java库编写Clojure包装器。 为了帮助我调试,我想记录对特定Java对象的所有调用 在从原始Java的角度研究如何实现这一点之后,我发现了记录Clojure对Java对象的所有方法调用,java,debugging,reflection,clojure,Java,Debugging,Reflection,Clojure,我正在为一些Java库编写Clojure包装器。 为了帮助我调试,我想记录对特定Java对象的所有调用 在从原始Java的角度研究如何实现这一点之后,我发现了Java.lang.reflect.Proxy类和Java.lang.reflect.InvocationHandler接口 这让我发现: 小提示:我必须将java.lang.reflect.Proxy.newProxyInstance更改为java.lang.reflect/newProxyInstance,以使其在我的REPL中工作,因
Java.lang.reflect.Proxy
类和Java.lang.reflect.InvocationHandler
接口
这让我发现:
小提示:我必须将java.lang.reflect.Proxy.newProxyInstance
更改为java.lang.reflect/newProxyInstance
,以使其在我的REPL中工作,因为newProxyInstance
是一个静态方法。已提供更正版本,以便您可以剪切和粘贴它
在这个版本中,代理上的调用方法首先会起作用:
youpi.core=> (.charAt (debug-proxy "foo") 2)
#object[java.lang.reflect.Method 0x53ce1eb0 public abstract char java.lang.CharSequence.charAt(int)] 2
=> \o
但遗憾的是,调用不接受任何参数的方法将不起作用:
youpi.core=> (.toUpperCase (debug-proxy "foo"))
IllegalArgumentException No matching field found: toUpperCase for class com.sun.proxy.$Proxy1 clojure.lang.Reflector.getInstanceField (Reflector.java:271)
java.lang.IllegalArgumentException: No matching field found: toUpperCase for class com.sun.proxy.$Proxy1
at clojure.lang.Reflector.getInstanceField(Reflector.java:271)
at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:315)
at youpi.core$eval5981.invokeStatic(form-init3685547971046661105.clj:1)
at youpi.core$eval5981.invoke(form-init3685547971046661105.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6890)
at clojure.core$eval.invokeStatic(core.clj:3105)
at clojure.core$eval.invoke(core.clj:3101)
at clojure.main$repl$read_eval_print__7408$fn__7411.invoke(main.clj:240)
at clojure.main$repl$read_eval_print__7408.invoke(main.clj:240)
<...>
at java.lang.Thread.run(Thread.java:745)
一个好的答案要么是修复我一直在使用的代码片段,要么是从Clojure那里提出一种实现相同目标的替代方法。这可能通过字节码操作很容易实现。有很多工具可以帮助实现这一点,但从来没有使用过足够的工具使推荐的Java动态代理与接口而不是类一起工作。在您的示例中,调用
charAt
很好,因为它是对代理实现的CharSequence
接口方法的调用。另一方面,调用toUpperCase
将不起作用,因为toUpperCase
是类String
上的一个方法,它不是由您的代理实现的(可以说)。@glts如果我理解正确,使用这种方法只允许对作为接口约定一部分实现的方法进行日志调用。方法arity是不相关的,(.length(调试代理“foo”)
确实工作正常。@EliZor是的。这是动态代理的一个限制,如果你想代理类,你确实必须降到我认为的字节码级别。值得一提的是,我已经在Clojure名称空间中手动编写了一系列代理函数,然后使用Clojure.tools.trace
来处理我当前关心的问题。我仍然对更优雅的方法感兴趣。
youpi.core=> (.toUpperCase (debug-proxy "foo"))
IllegalArgumentException No matching field found: toUpperCase for class com.sun.proxy.$Proxy1 clojure.lang.Reflector.getInstanceField (Reflector.java:271)
java.lang.IllegalArgumentException: No matching field found: toUpperCase for class com.sun.proxy.$Proxy1
at clojure.lang.Reflector.getInstanceField(Reflector.java:271)
at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:315)
at youpi.core$eval5981.invokeStatic(form-init3685547971046661105.clj:1)
at youpi.core$eval5981.invoke(form-init3685547971046661105.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6890)
at clojure.core$eval.invokeStatic(core.clj:3105)
at clojure.core$eval.invoke(core.clj:3101)
at clojure.main$repl$read_eval_print__7408$fn__7411.invoke(main.clj:240)
at clojure.main$repl$read_eval_print__7408.invoke(main.clj:240)
<...>
at java.lang.Thread.run(Thread.java:745)
(defn- debug-print-invoke [obj proxy m args]
(apply println m args)
(.invoke m obj args))
(defn debug-proxy [obj]
(java.lang.reflect.Proxy/newProxyInstance
(.. obj getClass getClassLoader)
(.. obj getClass getInterfaces)
(proxy [java.lang.reflect.InvocationHandler] []
(invoke
([proxy m] (debug-print-invoke obj proxy m []))
([proxy m args] (debug-print-invoke obj proxy m args))))))