Instrumentation介绍:
Java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序。这种监测和协助包括但不限于获取JVM运行时状态,替换和修改类定义等。JavaSE5中使用JVM TI替代了JVM PI和JVM DI。提供一套代理机制,支持独立于JVM应用程序之外的程序以代理的方式连接和访问JVM。Instrumentation 的最大作用就是类定义的动态改变和操作。在Java SE5 及其后续版本当中,开发者可以在一个普通Java程序(带有 main 函数的 Java 类)运行时,通过 – javaagent 参数指定一个特定的 jar 文件(包含 Instrumentation 代理)来启动 Instrumentation 的代理程序。
premain方式
在Java SE5时代,Instrument只提供了premain一种方式,即在真正的应用程序(包含main方法的程序)main方法启动前启动一个代理程序。例如使用如下命令:
[java]view plaincopy
java -javaagent:agent_jar_path[=options] java_app_name
[java]view plaincopy
java -javaagent:agent_jar_path[=options] java_app_name
可以在启动名为java_app_name的应用之前启动一个agent_jar_path指定位置的agent jar。 实现这样一个agent jar包,必须满足两个条件:
在这个jar包的manifest文件中包含Premain-Class属性,并且改属性的值为代理类全路径名。
代理类必须提供一个public static void premain(String args, Instrumentation inst)或 public static void premain(String args) 方法。
当在命令行启动该代理jar时,VM会根据manifest中指定的代理类,使用于main类相同的系统类加载器(即ClassLoader.getSystemClassLoader()获得的加载器)加载代理类。在执行main方法前执行premain()方法。如果premain(String args, Instrumentation inst)和premain(String args)同时存在时,优先使用前者。其中方法参数args即命令中的options,类型为String(注意不是String[]),因此如果需要多个参数,需要在方法中自己处理(比如用";"分割多个参数之类);inst是运行时由VM自动传入的Instrumentation实例,可以用于获取VM信息。
premain简单实例
1、先定义一个简单的Hello实例
public classTestMain {
public static void main(String[]args){
People people=newPeople();
people.sayHello();
}
}
其中 People类定义如下:
public classPeople {
public voidsayHello(){
System.out.println("hello !!!!");
}
}
2、编写一个MyAgent实例:
public classMyAgent {
/**
* 该方法是一个类作为agent类必备的
* @paramagentArgs
* @paraminst
*/
public static void premain(String agentArgs,Instrumentation inst){
//加入ClassFileTransfomer
inst.addTransformer(new PeopleClassFileTransformer());
}
}
PeopleClassFileTransformer类定义如下,主要通过javassist修改sayHello方法字节码:
public classPeopleClassFileTransformerimplementsClassFileTransformer {
/**
* 通过javassist修改字节码
* @paramloader
* @paramclassName
* @paramclassBeingRedefined
* @paramprotectionDomain
* @paramclassfileBuffer
* @return
* @throws IllegalClassFormatException
*/
@Override
public byte[]transform(ClassLoader loader,String className,Class classBeingRedefined,ProtectionDomain protectionDomain, byte[] classfileBuffer)throwsIllegalClassFormatException {
System.out.println("load class:"+className);
if("com/yao/intrumentation/People".equals(className)){
try{
//通过javassist修改sayHello方法字节码
CtClass ctClass= ClassPool.getDefault().get(className.replace('/','.'));
CtMethod sayHelloMethod=ctClass.getDeclaredMethod("sayHello");
sayHelloMethod.insertBefore("System.out.println(\"before sayHello----\");");
sayHelloMethod.insertAfter("System.out.println(\"after sayHello----\");");
returnctClass.toBytecode();
}catch(NotFoundException e) {
e.printStackTrace();
}catch(CannotCompileException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}
}
returnclassfileBuffer;
}
}
然后编写MAINIFEST.MF文件如下:
Manifest-Version: 1.0
Premain-Class:: com.yao.intrumentation.MyAgent
Created-By: 1.8.0_121
打包MyAgent文件成MyAgent.jar
3、在ideal运行配置界面中VM options输入-javaagent:E:/Java_Web/JavaSPI-master/target/classes/myagent.jar,即MyAgent.jar路径;
4、运行结果如下: