Java 使用 ASM 的字节码检测,MethodVisitor 为空

Java bytecode instrumentation using ASM, MethodVisitor is null

因此,使用下面编写的代码,我的输出是:

Starting application with the Agent
Visiting class: HelloWorld
Class Major Version: 51
Super class: java/lang/Object
Source: HelloWorld.java
Method: <init> desc = ()V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method: main desc = ([Ljava/lang/String;)V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method: foo desc = ()V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method ends here
Done instrumenting: HelloWorld

这让我很困惑。为什么我的 methodVisitor 会是 null?当 classVisitornull 时,ASM 源代码似乎只有 return null 用于 methodVisitor,这在我的情况下并非如此。

package com.amir.agent.instrumentor;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class amirClassVisitor {

    private byte[] outData = null;

    public amirClassVisitor() {
    }

    public void performInstrumentation(final String className,
                                       final byte[] classAsBytes) {
        final ClassVisitor cl = new ClassVisitor(Opcodes.ASM4) {
            @Override
            public void visit(final int version,
                              final int access,
                              final String name,
                              final String signature,
                              final String superName,
                              final String[] interfaces) {
                System.out.println("Visiting class: "+name);
                System.out.println("Class Major Version: "+version);
                System.out.println("Super class: " + superName);
                super.visit(version, access, name, signature, superName, interfaces);
            }

            @Override
            public void visitOuterClass(final String owner,
                                        final String name,
                                        final String desc) {
                System.out.println("Outer class: "+owner);
                super.visitOuterClass(owner, name, desc);
            }

            @Override
            public AnnotationVisitor visitAnnotation(final String desc,
                                                     final boolean visible) {
                System.out.println("Annotation: "+desc);
                return super.visitAnnotation(desc, visible);
            }

            @Override
            public void visitAttribute(final Attribute attr) {
                System.out.println("Class Attribute: " + attr.type);
                super.visitAttribute(attr);
            }

            @Override
            public void visitInnerClass(final String name,
                                        final String outerName,
                                        final String innerName,
                                        final int access) {
                System.out.println("Inner Class: " + innerName + " defined in " + outerName);
                super.visitInnerClass(name, outerName, innerName, access);
            }

            @Override
            public FieldVisitor visitField(final int access,
                                           final String name,
                                           final String desc,
                                           final String signature,
                                           final Object value) {
                System.out.println("Field: "+name+" "+desc+" value:"+value);
                return super.visitField(access, name, desc, signature, value);
            }

            @Override
            public void visitEnd() {
                System.out.println("Method ends here");
                super.visitEnd();
            }

            @Override
            public MethodVisitor visitMethod(final int access,
                                             final String name,
                                             final String desc,
                                             final String signature,
                                             final String[] exceptions) {
                final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
                System.out.println("Method: " +name+ " desc = " +desc+ " cv = " +this+ " and mv = " +mv);
                return mv;
            }

            @Override
            public void visitSource(final String source,
                                    final String debug) {
                System.out.println("Source: "+source);
                super.visitSource(source, debug);
            }

        };

        final ClassReader classReader = new ClassReader(classAsBytes);
        classReader.accept(cl, 0);
        System.out.println("Done instrumenting: " +className);

    }

    public byte[] result() {
        return outData;
    }

}

编辑:

我这样称呼这段代码:

public class ClassLoadInterceptor implements ClassFileTransformer {

    @SuppressWarnings("unchecked")
    public byte[] transform(final java.lang.ClassLoader loader,
                            final java.lang.String className,
                            final java.lang.Class classBeingRedefined,
                            final java.security.ProtectionDomain protectionDomain,
                            final byte[] classfileBuffer) throws IllegalClassFormatException {

        if (!(className.startsWith("java") || className.startsWith("sun") || className.startsWith("com/workday/agent"))) {
            WorkdayClassVisitor v = new WorkdayClassVisitor();
            v.performInstrumentation(className, classfileBuffer);
            System.out.println("\t Instrumenting : " +className);
            byte[] instrumented_class = v.result();
            writeOutClassFile("debug", className + ".class", classfileBuffer);
            writeOutClassFile("debug", className + "_instrumented" + ".class", instrumented_class);
            return instrumented_class;
        }

        return classfileBuffer;

    }

你打印出来的不是“my methodVisitor”,而是 super 调用 visitMethod 的值 return,换句话说,默认值 return编辑者 ClassVisitor.visitMethod:

Returns:
   an object to visit the byte code of the method, or null if this class visitor is not interested in visiting the code of this method.

由于抽象 class ClassVisitor 不执行任何操作,它没有兴趣访问该方法,因此 null 是完美的 return 值。

因此,由您来实例化一个新的 MethodVisitor 来实现您想要的行为。或者,由于您似乎打算检测 class,让您的 class 访问者委托给 ClassWriter which you pass to the super class constructor。这样您将继承可以自定义的复制行为。然后,super.visitMethod 将 return 一个非 null 方法访问者,它将复制该方法。