Java中有没有一种方法可以通过使用插装来拦截对象的创建?
我需要截获所有ClassNotFoundException或NoClassDefError的创建:问题是这些异常中的一些被一些库捕获,并在其他异常类型中重新引用,因此我无法检索类名。Java中有没有一种方法可以通过使用插装来拦截对象的创建?,java,exception,instrumentation,Java,Exception,Instrumentation,我需要截获所有ClassNotFoundException或NoClassDefError的创建:问题是这些异常中的一些被一些库捕获,并在其他异常类型中重新引用,因此我无法检索类名。 在Java中,有没有一种方法可以通过使用Intstrumentation来实现这一点?您可以编写自己的实现,并将您的逻辑应用到或其他可用的方法中ClassLoader是应用程序中ClassNotFoundException的常见来源。除非第三方库更改默认的类加载过程(例如OSGI),否则它们仍将调用您的类加载程序以
在Java中,有没有一种方法可以通过使用Intstrumentation来实现这一点?您可以编写自己的实现,并将您的逻辑应用到或其他可用的方法中
ClassLoader
是应用程序中ClassNotFoundException
的常见来源。除非第三方库更改默认的类加载过程(例如OSGI),否则它们仍将调用您的类加载程序以下示例向ClassNotFoundException
的构造函数添加插装,并在调用时执行System.err.println
我不确定如何从我认为您需要的检测调用回调
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import net.bytebuddy.jar.asm.ClassReader;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.ClassWriter;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
public class ClassNotFoundExceptionIntercept {
public static void premain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException {
inst.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader l, String name, Class<?> c, ProtectionDomain d, byte[] b)
throws IllegalClassFormatException {
if ("java/lang/ClassNotFoundException".equals(name)) {
return instrument(b);
}
return b;
}
}, true);
inst.retransformClasses(java.lang.ClassNotFoundException.class);
}
private static byte[] instrument(byte[] originalBytes) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassAdapter adapter = new ClassAdapter(cw);
ClassReader cr = new ClassReader(originalBytes);
cr.accept(adapter, ClassReader.SKIP_FRAMES);
return cw.toByteArray();
}
public static class ClassAdapter extends ClassVisitor implements Opcodes {
public ClassAdapter(ClassVisitor cv) {
super(ASM4, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
String[] exceptions) {
if ("<init>".equals(name)) {
MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
return new Wrapper(mv);
} else {
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}
}
private static class Wrapper extends MethodVisitor {
public Wrapper(MethodVisitor mv) {
super(Opcodes.ASM4, mv);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
mv.visitMethodInsn(opcode, owner, name, desc, itf);
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;");
mv.visitLdcInsn("Constructor invoked");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
}
}
并使用程序参数-javaagent:/home/adam/agent example.jar调用
这可以用一个包含异常的测试类来演示
public class Test {
public static void main(String[] args) {
try {
Class.forName("brexit");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出
Constructor invoked
Constructor invoked
Constructor invoked
java.lang.ClassNotFoundException: brexit
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at Test.main(Test.java:9)
这在ASM中是可能的。我以前做过一些事情来改变内置java类的行为——看到了吗?您可以转换显式创建这些类的实例的类。但我怀疑您是否能够拦截JVM本身创建的NoClassDefError
实例。我尝试过使用Java.lang.instrument.Instrumentation.addTransformer(ClassFileTransformer)编写Java代理,并使用System.out.println()记录所有类。不知何故,如果类没有被指令插入“看到”,您就不会发现异常。我认为这个类肯定有一些“特殊”的地方。是的,但是我怀疑JVM在内部构造错误时调用构造函数。我需要拦截这个异常,因为所有运行时类加载器都使用委托模型,如果无法加载类,子类加载器将调用父类加载器。您应该能够使用try/catch
包装对super
的调用,并在重新抛出异常之前发现异常。如果在类路径中有一个库直接使用我的类加载器的父类加载器,它将不会works@KarolDowbecki只有在自定义类上调用了loadClass
时,此选项才有效加载器。一旦您返回一个通过super
调用解析的类,它的所有依赖项都将通过父加载程序直接解析。非常感谢,我将尝试它
Constructor invoked
Constructor invoked
Constructor invoked
java.lang.ClassNotFoundException: brexit
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at Test.main(Test.java:9)