繁体   English   中英

Java 中有没有办法通过使用 Instrumentation 来拦截对象的创建?

[英]Is there a way in Java to intercept the creation of a object by using Instrumentation?

我需要拦截所有 ClassNotFoundException 或 NoClassDefError 的创建:问题是其中一些异常被某些库捕获并在其他异常类型中重新抛出,因此我无法检索类名。 有没有办法通过使用 Intstrumentation 在 Java 中做到这一点?

您可以编写自己的ClassLoader实现并在loadClass()或其他可用方法中应用您的逻辑。 ClassLoader是应用程序中ClassNotFoundException的常见来源。 除非第 3 方库更改默认类加载过程(例如 OSGI),否则它们仍将调用您的ClassLoader

下面的示例向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);
        }
    }

}

这需要像这样用 META-INF/MANIFEST.MF 编译成 JAR 文件

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM