Есть ли в Java способ перехватить создание объекта с помощью инструментария? - PullRequest
2 голосов
/ 04 февраля 2020

Мне нужно перехватить создание всех ClassNotFoundException или NoClassDefError: проблема в том, что некоторые из этих исключений отлавливаются некоторыми библиотеками и перебрасываются в другой тип исключения, поэтому я не могу получить имя класса. Есть ли способ сделать это в Java с помощью Intstrumentation?

Ответы [ 2 ]

1 голос
/ 04 февраля 2020

Вы можете написать свою собственную реализацию ClassLoader и применить свою логику c в loadClass() или другим доступным способом. ClassLoader - это обычный источник ClassNotFoundException в приложении. Если сторонние библиотеки не изменят процесс загрузки классов по умолчанию (например, OSGI), они все равно будут вызывать ваш ClassLoader.

0 голосов
/ 05 февраля 2020

Следующий пример добавляет инструментарий в конструктор 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)
...