作者:嘉信永顺_232 | 来源:互联网 | 2013-05-23 08:54
本文主要引导你认识何为JVMAgent,何为bytecodeinstrumentation,同时讲述如何使用API为bytecodeinstrumentation创建一个agent
JVM Agent是一个程序,运行在同一个进程中。一个Agent可以接受来自JVM上的事件,也可以在JVM上查询自身的信息,或者控制应用程序的运行和修改应用程序。
bytecode instrumentation是改变了编译器上生成的一个类的bytecode,它有多种多样的漂亮很酷的东西,如:计算一个方法被执行需要多长时间、改变它的执行流程等无限的可能性。用户可以添加/改变应用程序的字节码,自从有了这个功能后,就不需要修改整个应用程序源。bytecode instrumentation不仅可以为JVM Agents提供上述功能,事实上,它在JVM上还能做各种各样的有趣又疯狂的事情。
JVMTI(JVM Tool Interface是JVM所提供的本地编程接口,是JVMPI(Java Virtual Machine Profiler Interface)和JVMDI(Java Virtual Machine Debug Interface)的更新版本。JVMTI还定义了包括在Java编程语言的过程中的instrumentation API (java.lang.instrument),它可以让JVM上bytecode instrumentation直接在java API上工作。
这里我们的目标是bytecode instrumentation,它能更好支持Java API,因为Java本身处理字节码更容易,也有部分原因是由于众所周知的开源库(如:Javassist,ASM,BCEL)已经可以处理这些问题。
下面具体看看写一个instrumentation agent的一些步骤:
步骤一:The Agent Premain Class
在JVM启动程序期间,如果想要加载agent,那么它必须实现一个类似于公共静态空的main方法,premain方法有两种可能的签名:
public static premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);
这两种方法都可以有效的被执行,如果agent在JVM启动后加载,那么它还需要一个agentmain方法,否则,agentmain方法将不会被调用。
如今的情况,我们将会使用第一种签名:
public static void premain(String agentArgs, Instrumentation instrumentation);
理由是:需要把instrumentation参数通过JVM传递给agent,以便于注册一个或多个ClassFileTransformer来允许用户重新定义一个类字节码。
如下代码所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package my.agent;
import java.lang.instrument.Instrumentation;
public class MyAgent {
//agent start-up hook
public static void premain(String args, Instrumentation inst) throws Exception {
System.out.println("Loading Agent..");
inst.addTransformer(newMyTransfomer());
}
}
|
步骤二:The ClassFileTransfomer
正如上提到的ClassFileTransfomer(MyTransformer是一个实现了接口)允许用户改变/重新定义类字节码,这些操作需要在获得定义之后才能操作。
ClassFileTransfomer是个接口,需要用户自己实现方法:
1
|
byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer);
|
一旦用户注册一个属于自己的ClassFileTransformer :
1
|
inst.addTransformer(newMyTransfomer())
|
JVM将调用MyTransformer中的transform方法重新开始定义程序。
这里是类MyTransformer实现了ClassFileTransformer接口,输出类名以及在JVM中进行加载的类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package my.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
/**
* Dummy sample ClassFileTransformer
* @author rafael oltra
*
*/
public class MyTransfomer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className,
Class> klass, ProtectionDomain domain, byte[] klassFileBuffer)
throws IllegalClassFormatException {
System.out.println(className +" is about to get loaded by the ClassLoader");
returnnull;
}
}
|
步骤三:MANIFEST.MF
现在需要为agent创建一个有效的MANIFEST.MF文件,这样做的原因是不仅需要告知JVM、包含agent的JAR(下一步需要做的内容),而且需要告诉agent包含premain方法以及其它方面,agent的文件的内容有:
Manifest-Version: 1.0
Agent-Class: my.agent.MyAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: my.agent.MyAgent
-
确保MANIFEST.MF文件和META-INF文件夹在JAR中。
步骤四:Building the agent
agent的当前状态,没有任何外界依赖,用户可以按自己喜欢的方式在META-INF文件夹中建立MANIFEST.MF文件。如果用户不想建立的话,可以直接下载JAR文件。
步骤五:Running the agent within the JVM – How to and output
想要在JVM中实际运行agent,你需要传递-javaagent参数到JVM中,执行Java应用程序通常使用同种方式但需要通过传递-javaagent参数,如:
java -javaagent:PATH_TO_AGENT.jar -jar myapplication.jar
文章来源:rafaeloltra.com