【发布时间】:2020-07-26 14:11:00
【问题描述】:
我想做的是:我有一个ClassVisitor适配器,当进入visit方法时,我想知道AncestorClass这个类是不是那个类的祖先类?我曾尝试像这样使用反射 (Class.forname(...)):
MyTransformer.class:
public class MyTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (className == null){
return classfileBuffer;
}
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
CollectionCVAdapter mca = new MyCVAdapter(cw, className);
cr.accept(mca, 0);
return cw.toByteArray();
}
}
MyCVAdapter.class:
class MyCVAdapter extends ClassVisitor {
private String className;
private boolean isDescendantClass;
CollectionCVAdapter(ClassVisitor classVisitor, String className){
super(Opcodes.ASM5, classVisitor);
this.className = className;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// check if this class is a descendant class of AncestorClass.class
try{
Class superClass = Class.forName(superName.replace("/", "."));
do {
if (superClass == AncestorClass.class) {
this.isDescendantClass= true;
break;
}
superClass = superClass.getSuperclass();
} while (superClass != null);
}catch (Exception e){
e.printStackTrace();
}
super.visit(version, access, name, signature, superName, interfaces);
}
...
}
但问题是我还想检测已检测类的父类。我发现一旦Class.forName()加载了父类,父类就不会进入transform方法。这意味着不能再检测父类。那么是否有任何替代方法可以确定该类是否是AncestorClass 的后代类?
【问题讨论】:
-
第一:使用
ClassReader.getSuperName()。第二:用loader.getResourceAsStream(superName + ".class")加载类文件。用 ClassReader 解析它。 -
使用经典的
ClassLoader架构,没有万无一失的方法。getResourceAsStream(internalName + ".class")是在不加载类的情况下获取字节码的最佳方式,只要特定的类加载器实现始终如一地提供类文件的内容作为资源(所有标准加载器都这样做)。对于模块化代码,代码总是由委派给ModuleReader的标准加载器加载以获取字节码,因此getResourceAsStream将始终与loadClass在同一个ModuleLayer内保持一致。 -
@Holger 你能解释更多关于
ModuleReader的信息吗?我以前没听说过。在transform方法中,我可以获得ClassLoader参数,以使用getResourceAsStream。但在其他情况下,当我没有可用的ClassLoader并且我想使用getResourceAsStream来获取字节码时,我通常使用Class.forName(className).getClassLoader().getResourceAsStream。实际上,我不确定哪个加载器可以给我想要的字节码。还有更优雅的方式吗? -
由于您正在实现
ClassFileTransformer,因此您应该使用作为transform方法的第一个参数提供的ClassLoader。如果是null,则使用ClassLoader.getSystemResourceAsStream(…)。 -
但是如果你检查的基类不是系统类,你可以排除系统类成为它的子类,所以当类加载器参数是
null时你可以直接跳过。同样,只要类名以java/开头,您就可以使用快捷方式。
标签: java jvm instrumentation java-bytecode-asm javaagents