前世今生
-
Instrumentation
-
在JAVA 5中,我们可以通过java代码,即java.lang.instrument做动态Instrumentation,它把Java的instrument功能从本地代码中解放出来,使之可以用java代码的方式解决问题。使用Instrumentation,开发者可以构建一个独立于应用程序的代理程序(agent),用来监测和协助运行在JVM上的程序,甚至能够替换和修改某些类的定义。有了这样的功能,开发者就可以实现更为灵活的运行时虚拟机监控和Java类操作了,这样的特性实际上提供了一种虚拟机级别支持的AOP实现方式,使得开发者无需对JDK做任何升级和改动,就可以使用某些AOP的功能了。
-
在JAVA SE6中,通过Java ToolAPI中的attach方式,我们可以很方便的在运行过程中动态的设置加载代理类,以达到instrumentation的目的。而不用像在Java SE 5中,必须在运行前用命令行参数或者系统参数来设置代理类。
-
java.lang.instrument包的具体实现,依赖于JVMTI(Java Virtual Machine Tool Interface)。JVMTI是一套由Java虚拟机提供的,为JVM相关的工具提供的本地编程接口集合。除开Instrumentation功能外,JVMTI还在虚拟机内存管理,线程控制,方法和变量操作等等方面提供了大量有价值的函数。
-
工作流程(以premain为例)
-
创建代理 - 通过实现premain方法来创建 Java代理。这个方法是代理的入口,允许对字节码进行操作和插装。
-
指定代理 - 使用-javaagent命令行选项启动JVM,指定代理JAR文件的路径。在应用程序类加载之前调用代理的premain方法。
-
插装 - 在premain方法中,使用 Instrumentation对象向 JVM 添加 ClassFileTransformer。ClassFileTransformer 实现允许在类加载到 JVM 时拦截和修改字节码。
-
类转换 - 每当加载类时,ClassFileTransformer 的 transform 方法被调用,允许你根据需求修改字节码。
-
应用修改 - 在 transform 方法中,你可以检查类字节码,进行必要的修改,并将修改后的字节码返回给 JVM。
-
类加载 - 一旦应用修改,类就会以 ClassFileTransformer 所做的更改被加载到 JVM 中。
-
-
接口介绍
public interface Instrumentation {
/**
* 注册一个Transformer,从此之后的类加载都会被Transformer拦截。
* Transformer可以直接对类的字节码byte[]进行修改
*/
void addTransformer(ClassFileTransformer transformer);
/**
* 对JVM已经加载的类重新触发类加载。使用的就是上面注册的Transformer。
* retransformClasses可以修改方法体,但是不能变更方法签名、增加和删除方法/类的成员属性
*/
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
/**
* 获取一个对象的大小
*/
long getObjectSize(Object objectToSize);
/**
* 将一个jar加入到bootstrap classloader的 classpath里
*/
void appendToBootstrapClassLoaderSearch(JarFile jarfile);
/**
* 获取当前被JVM加载的所有类对象
*/
Class[] getAllLoadedClasses();
}
增强开发
-
开发流程
-
选择加载方式
-
启动时加载 - premain
-
/**
* 以vm参数的形式载入,在程序main方法执行之前执行
* 其jar包的manifest需要配置属性Premain-Class