动态字节码检测 - 问题
我有一个无法解决的问题。 假设我们有以下两个类和继承关系:
public class A {
}
public class B extends A {
public void foo() {}
}
我想检测附加代码,使其如下所示:
public class A {
public void print() { }
}
public class B extends A {
public void foo() { print(); }
}
为了实现此目标,我基于 java.lang.instrument
包,使用带有我自己的类文件转换器的代理。 该机制也称为动态字节码检测。
到目前为止小菜一碟。 现在,我的测试方法执行以下操作:
代码:
B b = new B();
b.foo();
由于检测包中存在以下限制,这不起作用:当调用 new B()
时,检测以类 B 开始并以 a 结束加载被操作的类时出现编译错误,因为超类 A 还没有 print() 方法! 问题是我是否以及如何在类 B 之前触发类 A 的检测。我的 classfiletransformer 的 Transform() 方法应该与类 A 一起显式调用! 所以我开始阅读并遇到了这个:
java.lang.instrument.ClassFileTransformer.transform()
's javadoc 说:
变压器将被要求 每个新的类定义和每个 类的重新定义。 请求 新的类定义是用 类加载器.defineClass。 请求 对于类重新定义是用 Instrumentation.redefineClasses 或其 本机等效项。
Transform 方法附带了一个类加载器实例,所以我想,为什么不自己用类调用 loadClass
方法(loadClass
调用 defineClass
) A 当 B 的检测开始时。 我预计仪器方法将被调用,但遗憾的是事实并非如此。 相反,类 A
是在没有检测的情况下加载的。 (代理不会拦截加载过程,尽管它应该这样做)
有什么想法,如何解决这个问题? 您是否明白为什么操作某些字节码的代理不可能手动加载另一个类(然后希望也通过该/任何代理发送)的原因?
请注意,以下代码可以正常工作,因为 A 在操作 B 之前已加载并检测。
A a = new A();
B b = new B();
b.foo();
多谢!
I have a problem I am not able to solve. Let's assume we have the following two classes and an inheritance relationship:
public class A {
}
public class B extends A {
public void foo() {}
}
I want to instrument additional code such that it looks as follows:
public class A {
public void print() { }
}
public class B extends A {
public void foo() { print(); }
}
In order to achieve this goal, I based my implementation on the java.lang.instrument
package, using an Agent with my own class file transformer. The mechanism is also referred to as dynamic bytecode instrumentation.
Piece of cake so far.
Now, my test method does the following:
Code:
B b = new B();
b.foo();
This does not work due to the following restriction in the instrumentation package: when calling new B()
, the instrumentation starts with class B and ends up in a compilation error when loading the manipulated class as the super class A has no print() method yet! The question arises if and how I can trigger the instrumentation of class A before class B. The transform() method of my classfiletransformer should be invoked with class A explicitly! So I started reading and bumped into this:
The java.lang.instrument.ClassFileTransformer.transform()
's javadoc says:
The transformer will be called for
every new class definition and every
class redefinition. The request for a
new class definition is made with
ClassLoader.defineClass. The request
for a class redefinition is made with
Instrumentation.redefineClasses or its
native equivalents.
The transform method comes along with a class loader instance, so I thought, why not calling the loadClass
method (loadClass
calls defineClass
) myself with class A when the instrumentation of B has started.
I expected that the instrument method will be called as a result but sadly this was not the case. Instead the class A
was loaded without instrumentation. (The agent does not intercept the load process although it is supposed to)
Any ideas, how to solve this problem? Do you see a reason why it is not possible that an agent that manipulates some bytecode cannot manually load another class that is then hopefully also send through that/any agent?
Note that the following code works properly since A was loaded and instrumented before B is manipulated.
A a = new A();
B b = new B();
b.foo();
Thanks a lot!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当我在 Sun 1.6.0_15 和 1.5.0_17 JRE 上先转换 B 时,我没有看到任何问题(我使用 ASM< /a>)。 我会通过在外部运行转换代码并检查生成的类(例如使用 javap)来仔细检查转换代码。 我还会检查您的类路径配置,以确保由于某种原因 A 不会在您的代理之前加载(也许使用 getAllLoadedClasses)。
编辑:
如果您在代理中加载类
A
,如下所示:...然后抛出异常:
这是有道理的 -
A
成为代理的依赖项,并且它会对于代理来说,检测自己的代码是没有意义的。 你会得到一个无限循环,导致堆栈溢出。 因此,A
不会被ClassFileTransformer
处理。为了完整起见,这是我的测试代码,可以正常工作。 如前所述,它取决于 ASM 库。
代理:
A
的方法注入器:B
的方法替换器:通用基础代码:
对于一些可见的结果,我添加了
System.out.println('X ');
到A.print()
。当运行此代码时:
...它会产生以下输出:
I did not see any issues when I transformed B before A on the Sun 1.6.0_15 and 1.5.0_17 JREs (I used ASM). I would double-check the transformation code by running it externally and inspecting the resultant classes (e.g. with javap). I'd also check your classpath configuration to ensure A isn't loaded before your agent for some reason (perhaps check in your premain with getAllLoadedClasses).
EDIT:
If you load class
A
in your agent like this:...then an exception is thrown:
This makes sense -
A
becomes a dependency of the agent and it would not make sense for the agent to instrument its own code. You'd get an infinite loop that resulted in a stack overflow. Therefore,A
is not processed by theClassFileTransformer
.For completeness, here is my test code that works without problem. As mentioned, it depends on the ASM library.
The agent:
Method injector for
A
:Method replacer for
B
:Common base code:
For some visible results, I added a
System.out.println('X');
toA.print()
.When run on this code:
...it produces this output: