Следующий пример добавляет инструментарий в конструктор 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);
}
}
}
Это необходимо собрать в файл JAR с META-INF / MANIFEST. MF как этот
Manifest-Version: 1.0
Premain-Class: ClassNotFoundExceptionIntercept
Agent-Class: ClassNotFoundExceptionIntercept
Can-Retransform-Classes: true
Can-Redefine-Classes: true
и вызов с аргументом программы -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)