突然想起应该看看JDK自带的java.lang.instrument这个包,昨天花了点时间学习了一下。在JDK 5之前,相应的包是属于Java Virtual Machine Profiler Interface (JVMPI),在JDK 5之后,改为Java Virtual Machine Tool Interface (JVM TI),这个包就属于JVM TI的一个alternate。在页面:
http://docs.oracle.com/javase/6/docs/technotes/guides/instrumentation/index.html
中,有对这个包的详细介绍。
上面这个页面链接到这个包的详细介绍:http://docs.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html
从这个页面中,我们看到,这个包主要的功能是:Provides services that allow Java programming language agents to instrument programs running on the JVM. The mechanism for instrumentation is modification of the byte-codes of methods.
从上面详细页面中,可以看到该包有ClassFileTransformer和Instrumentation,今天我们看一下怎么通过ClassFileTransformer这个接口来实现动态修改类代码。
依然在这个页面中,"An agent is deployed as a JAR file. An attribute in the JAR file manifest specifies the agent class which will be loaded to start the agent. For implementations that support a command-line interface, an agent is started by specifying an option on the command-line."
其实这句话就说的很清楚了,我们要实现动态修改类代码这个功能,需要实现一个agent,并且这个agent应该以Jar包的形式,通过命令行调用。
这里直接上代码解释比较好,首先,我们写的Agent要实例化ClassFileTransformer这个接口:
public class test implements ClassFileTransformer
其次,按照上面页面中的介绍,The agent class must implement a public static premain
method similar in principle to themain
application entry point. 这个agent类需要实现一个premain方法:
public static void premain(String options, Instrumentation ins) { if (options != null) { System.out.printf(" I've been called with options: \"%s\"\n", options); } else System.out.println(" I've been called with no options."); ins.addTransformer(new test()); }
然后,ClassFileTransformer这个接口定义了一个方法:transform,我们需要对其进行实例化:
public byte[] transform(ClassLoader loader, String className, Class cBR, java.security.ProtectionDomain pD, byte[] classfileBuffer) throws IllegalClassFormatException { if(!className.endsWith("HelloWorld")) return(null); String line=""; for(int i=0;i<classfileBuffer.length;i++){ line +=Byte.toString(classfileBuffer[i])+" "; if(line.length()>60){ System.out.println(line); line=""; } if(classfileBuffer[i]==(byte)'6') classfileBuffer[i]=(byte)'7'; } System.out.println(line); System.out.println("The number of bytes in HelloWorld: "+classfileBuffer.length); return(classfileBuffer); }
其实这段代码的作用很简单,就是判断如果执行的类名称为HelloWorld的时候,print出这个类的内容,同时,如果HelloWorld类代码中有"6"这个字符,其将被自动替换为"7"。
为了验证上面的功能,我们生成一个HelloWorld类:
public class HelloWorld{ public static void main(String[] args) { System.out.println("The number six is 6"); } }
如果要运行上面的test类,需要生成一个Jar包,在Eclipse中生成Jar包的方法很简单:我们首先需要生成一个MANIFEST.MF文件(保存在工程的任意位置均可),需要在这个文件中指定代理的名称:
Manifest-Version: 1.0 Premain-Class: test
其次,在工程名上点右键——"Export"——选择Java目录下的JAR File——一直点击Next直到需要设置MANIFEST.MF文件为止,选择"Use existing manifest from workspace"——然后选择刚才创建的MANIFEST.MF文件,再生成Jar包就可以了。
我们把生成的Jar包和HelloWorld类的Class文件放到同一个目录下,运行:
java -javaagent:test.jar="HelloWorld.main" HelloWorld
然后就可以得到如下图所示的运行结果:
说明通过test这个agent,已经动态更改了HelloWorld这个类的内容。到这里我们的目的也就实现了。
最后附上test.java的源代码:
import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; /** * A trivial example program that basically just says hello! */ public class test implements ClassFileTransformer { public static void premain(String options, Instrumentation ins) { if (options != null) { System.out.printf(" I've been called with options: \"%s\"\n", options); } else System.out.println(" I've been called with no options."); ins.addTransformer(new test()); } public byte[] transform(ClassLoader loader, String className, Class cBR, java.security.ProtectionDomain pD, byte[] classfileBuffer) throws IllegalClassFormatException { if(!className.endsWith("HelloWorld")) return(null); String line=""; for(int i=0;i<classfileBuffer.length;i++){ line +=Byte.toString(classfileBuffer[i])+" "; if(line.length()>60){ System.out.println(line); line=""; } if(classfileBuffer[i]==(byte)'6') classfileBuffer[i]=(byte)'7'; } System.out.println(line); System.out.println("The number of bytes in HelloWorld: "+classfileBuffer.length); return(classfileBuffer); } }
同时附带上jar包和HelloWorld的class文件:
http://files.cnblogs.com/quyu/instrument-ClassFileTransformer.rar