java.lang.instrument包初步学习

上周给小组几位师弟、实习生开会的时候,又谈到instrumentation这个现在还没解决的问题。我看过的论文中,对于Java的Instrumentation,大部分研究都在用SootJChord,后来想起,看过的论文中还有用BCELJavassist的。前两者我们花了很长时间学习,但是到现在效果并不是很好。

突然想起应该看看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 the main 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文件:

https://files.cnblogs.com/quyu/instrument-ClassFileTransformer.rar

posted on 2011-12-14 17:01  qysh123  阅读(3070)  评论(0编辑  收藏  举报

导航