我正在尝试在javaagent to change the class that is being constructed(sun/misc/URLClassPath
)中使用ASM到从中继承的另一个(fommil/URLClassPath
)并覆盖所有方法。我知道目标类(java/net/URLClassLoader
),我是retransforming
,是唯一创建sun/misc/URLClassPath
并且只在其构造函数中创建的东西。
基本理念是这样的:
@Override
public MethodVisitor visitMethod(int access,
String name,
String desc,
String signature,
String[] exceptions) {
MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions);
return new MethodVisitor(Opcodes.ASM5, visitor) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (opcode == Opcodes.INVOKESPECIAL && "sun/misc/URLClassPath".equals(owner) && "<init>".equals(name)) {
super.visitMethodInsn(opcode, "fommil/URLClassPath", name, desc, itf);
} else {
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
};
}
我可以在println
的构造函数中添加fommil/URLClassPath
,看看它是否正在构建中!
然而,没有调用fommil.URLClassPath
个方法。只调用超类上的方法。
即使我更改了上述代码,不仅invokespecial
/ <init>
,而且所有对URLClassPath
的调用都会被重写,以便他们的invokevirtual
指向我的班级,我的方法仍未调用。我甚至尝试过为URLClassLoader
的所有内部类做这个。
那么为什么invokevirtual
没有找到覆盖方法,即使它们被重新转换了?我正在做什么 - 改变正在构建的东西的类型 - 从根本上说是不可能的?如果是这样,有人可以解释为什么?
我知道修正核心JDK课程是非常邪恶的,但坦率地说,我真的没有其他选择。
唯一要做的就是检测试图实例化URLClassLoader
的所有类,并让它们进入内部ucp
字段并将其替换为我的实现。
答案 0 :(得分:0)
我明白了!
考虑这个简单的代码
import java.util.*;
class Baz {
public void baz() {
List list = new ArrayList();
}
}
产生
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: return
与
相比import java.util.*;
class Baz {
public void baz() {
List list = new LinkedList();
}
}
产生
0: new #2 // class java/util/LinkedList
3: dup
4: invokespecial #3 // Method java/util/LinkedList."<init>":()V
7: astore_1
8: return
请注意其中包含类型的new
关键字!
问题是如果想要更改构造函数,还必须更改返回的类型定义。即添加此
@Override
public void visitTypeInsn(int opcode, String type) {
if ("sun/misc/URLClassPath".equals(type))
super.visitTypeInsn(opcode, "fommil/URLClassPath");
else
super.visitTypeInsn(opcode, type);
}