在类加载之前修改类的字节码

在类加载之前修改类的字节码

在类加载之前修改类的字节码可使用Instrumentation的addTransformer方法为Instrumentation注册一个类转换器,由类转换器负责修改类的字节码,将修改后的字节码交给JVM去加载。类转换器接口ClassFileTransformer的定义如下:

public interface ClassFileTransformer {
  byte[] transform(ClassLoader         loader,
                String              className,
                Class<?>            classBeingRedefined,
                ProtectionDomain    protectionDomain,
                byte[]              classfileBuffer)
        throws IllegalClassFormatException;
}

transform方法参数说明:

◆loader:加载该类的类加载器,如果为null则使用的类加载器为BootstrapLoader加载器;

◆className:类的名称;

◆classBeingRedefined:如果该方法是由类重新定义触发的,那么该参数就是需要重新定义的类;如使用Instrumentation的redefineClasses方法重定义类;

◆protectionDomain:被定义或重新定义的类的保护域;

◆classfileBuffer:class文件读取到内存中的字节数组,不可修改;

transform方法返回值为class字节数组。如果不需要修改该类,则返回null,如果需要修改该类,则将修改后的class字节数组返回,JVM就会加载修改后的class。

实现在类加载之前修改类的字节码,我们需要先创建一个类转换器,即实现ClassFileTransformer接口的类,这个类转换器负责将类的字节数组转换为插桩后的类的字节数组。类转换器BusinessClassFileTransformer的实现如代码清单7-3所示。

代码清单7-3 BusinessClassFileTransformer类

public class BusinessClassFileTransformer implements ClassFileTransformer {

    @Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
 ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
        // 过滤掉不需要插桩的类
        if (className.startsWith("java")
|| className.startsWith("sun")) {
            return null;
        }
        // 过滤接口
        if (classBeingRedefined.isInterface()) {
            return null;
        }
        // 返回修改后的类的字节数组
        return ClassInstrumentationFactory.modifyClass(classfileBuffer);
    }
}

在BusinessClassFileTransformer的transform方法中,我们可以过滤掉一些不需要插桩的类,如包名以“java”或“sun”开头的类,接口也不需要插桩。如果当前类需要进行插桩,则调用ClassInstrumentationFactory的modifyClass方法修改类的字节码。

编写完类转换器之后,我们需要修改premain方法,为premain方法的instrumentation参数注册这个类转换器,如代码清单7-4所示。

代码清单7-4 premain方法

public class MyJavaAgent {

    public static void premain(String agentOps, Instrumentation instrumentation) {
        System.out.println("premain function run...");
// 注册类转换器
        instrumentation.addTransformer(new BusinessClassFileTransformer());
    }

}

将类转换器注册到Instrumentation之后,每个类在加载之前,注册的类转换器的transform方法就都会被调用到。