我熟悉使用代理拦截方法调用的各种方法,但我想知道是否有一种方法可以使用 Javassist 或 ASM 等库来检测某些代理上的字段访问/取消引用?例如:
void detectFieldName(Function<Foo, Supplier<String>> f) {
Foo fooProxy = createFooProxy();
f.apply(fooProxy);
}
detectFieldName((Foo foo) -> foo.bar);
理想情况下,我想知道名为 bar
的字段已取消引用。
最佳答案
看看您更新的用例:lambda 被脱糖为合成(编译器生成)方法,并带有一个通过生成的方法转发接口(interface)调用的函数对象(我还没有具体研究这是如何实现的,但我认为Brian Goetz 已经谈到过这一点)。您只需查看该方法的字节码(从类文件加载;一些 ASM 示例代码执行此操作)并读取字段访问。不需要仪器。
请注意,您无法创建代理来查看字段访问;字段访问是在 lambda 方法(或者更一般地说,加载字段的地方)中执行的,而不在 Foo 中执行任何代码。事实上,如果您只想获取字段名称,您甚至不需要调用 lambda,并且如果您使用 Foo 代理只是为了调用,则不需要代理。
<小时/>我不知道有什么方法可以像 java.lang.reflect.Proxy
拦截方法调用那样轻松地拦截字段访问。
getfield
和 putfield
字节码使用对类和字段名称进行编码的符号描述符,因此您可以使用 Java agent在每次加载和存储之前或之后添加方法调用,传递正在加载/存储的字段名称、对象和值。 (如果您只对字段的子集(例如特定类的所有字段)感兴趣,则这种方法效果最佳。)根据您的需要,您可能还必须通过使用 java.lang.Instruction 来识别对字段的反射访问。 lang.reflect.Field
,MethodHandles.Lookup.findGetter/Setter返回的句柄等(可能涉及过程间分析或用于构建字段名称的字符串操作的推理等) )。您还可以尝试在库调用某些特定于 JVM 的 native 功能“之前”进行检测,但这会将您与一个 JVM 实现联系起来,并且如果 JVM 内在反射调用(特殊情况下的代码生成),您的检测可能会被跳过。
如果您愿意编写C代码,您可以使用JVM工具接口(interface)watched field functions 。这似乎是获取信息的最简单方法,但是用它来做有趣的 Java 级别的事情却比较困难(尽管您可以从 JVMTI 回调到 Java 支持库)。
关于java - 使用 Javassist 或 ASM 拦截字段访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23839945/