我们目前使用 premain 在加载所有类之前对其进行转换。 但我们希望控制转换,这意味着我们可以删除/重新添加在方法中插入的代码。
所以我们找到了重新转换,但我不太理解它。
- 我们可以重新转换加载的类吗?
- 如果可以做到,重新转换的类的实例是否会运行重新转换的代码?
- 如果做不到,是否有任何技术可以在运行时更改某些指定方法的代码。
已更新
例如,我们有类 A。类 A 有两个方法 foo() 和 bar():
void foo() {
while(true) {
bar();
}
}
void bar() {
System.out.println("a");
}
我们调用 Instrument.retransformClasses(A.class)。并将 bar() 更改为:
void bar() {
System.out.println("b");
}
如果有一个线程已经在调用 foo(),我们可以得到输出:
...
a
a
a
b
b
b
...
如果没有,有什么办法可以实现吗?
最佳答案
因此,您已经有了一个 ClassFileTransformer
来在类的字节码加载并安装到 JVM 之前对其进行转换。现在您希望沿着 JVM 生命周期再次重新转换,不是吗?
嗯,重新转换可以通过Instrumentation来完成,通过Instrumentation.retransformClasses方法。但首先,您必须通过调用Instrumentation.isRetransformClassesSupported来确保支持重新转换。 .
这次改造什么时候生效?根据javadoc:
If a retransformed method has active stack frames, those active frames continue to run the bytecodes of the original method. The retransformed method will be used on new invokes.
这意味着,在您的示例中,foo()
重复调用bar()
。如果同时重新转换bar
,则转换后的代码将影响调用bar
的下一次迭代。
我将为您的示例添加更多复杂性:
void foo() {
while(true) {
bar();
}
}
void bar() {
myRetransform();
System.out.println("a");
}
void myRetransform() {
// This is where a retransformation of class A, method bar, is triggered.
}
当触发重新转换时,当前栈帧为:
- myRetransform
- 酒吧
- 富
有一个 Activity 的调用堆栈帧,其中包含 bar
,因此当 myRetransform
返回时,它仍会在 system.out 中打印“a”。
然后,bar
将返回并离开当前堆栈帧,如下所示:
- 富
而现在是最后一次触发的转换生效的时间。即:在下一次调用 bar
时。
关于java - 我们可以在类加载后重新转换类吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39140148/