JavaDemo——关于javaagent的使用
一、使用javaagent参数启动java程序使用方法:1、agent类需要实现特定方法,实现public static void premain(String args, Instrumentation instrumentation) 或者public static void premain(String args)(优先两个参数的方法,找不到则执行一个参数的方法)2、jar包里的MF文件需要
一、使用javaagent参数启动java程序
使用方法:
1、agent类需要实现特定方法,实现
public static void premain(String args, Instrumentation instrumentation) 或者 public static void premain(String args)
(优先两个参数的方法,找不到则执行一个参数的方法)
2、jar包里的MF文件需要指定Premain-Class为agent类(Can-Redefine-Classes和Can-Retransform-Classes通常也指定为true)
3、将agent类和MF文件打成jar包
4、启动java程序时使用-javaagent参数指定代理jar包(java程序可以用java直接启动主类class也可以-jar启动jar包,-javaagent位置放-jar前面,多个javaagent按序执行)
Demo:
Hello.java、hello.MF是用来测试的java程序,hello.bat脚本用来编译打包;
MyAgent.java、myagent.MF、myagent.bat用来编译打包代理类的;
agentcls文件夹用来放置agent类的class文件并用作打包目录,并且提前将javassist.jar解压进去(我的agent类使用了javassist修改类内容,并且我想打成一个jar包);
Hello.java代码:
public class Hello {
public static void main(String[] args) {
System.out.println("HELLO");
Count c = new Count();
System.out.println(c.count(1));
}
}
class Count{
public int count(int x){
return x + 1;
};
};
hello.MF文件:
Manifest-Version: 1.0
Created-By: FlyLikeButterfly
Main-Class: Hello
hello.bat脚本:
javac Hello.java
jar cvfm hello.jar hello.MF Hello.class Count.class
del Hello.class
del Count.class
pause
MyAgent.java代码:
package test;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class MyAgent {
public static void premain(String args, Instrumentation instrumentation) {
ClassFileTransformer mytransformer = new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("className:" + className);
if("Count".equals(className)) {
System.out.println("==find class.");
ClassPool pool = ClassPool.getDefault();
InputStream is = new ByteArrayInputStream(classfileBuffer);
try {
CtClass cls = pool.makeClass(is);
CtMethod m = cls.getDeclaredMethod("count");
m.setBody("{return $1 + 10;}");
return cls.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
}
return classfileBuffer;
}
};
instrumentation.addTransformer(mytransformer);
}
public static void premain(String args) {
System.out.println("premain.");
}
}
myagent.MF文件:
Manifest-Version: 1.0
Created-By: FlyLikeButterfly
Premain-Class: test.MyAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
myagent.bat脚本:
javac -cp agentcls -d agentcls MyAgent.java
jar cvfm myagent.jar myagent.MF -C agentcls .
pause
执行测试:
先生成测试需要的jar包:执行hello.bat生成hello.jar,执行myagent.bat生成myagent.jar;
先测试hello.jar包,再加入javaagent测试:
java -jar hello.jar
java -javaagent:myagent.jar -jar hello.jar
执行结果:
二、使用attach机制让运行中的java程序加载agent
使用方法:
1、agent需要实现特定方法,实现
public static void agentmain(String args, Instrumentation instrumentation) 或者 public static void agentmain(String args)
(跟premain一样优先两个参数的)
2、MF文件指定Agent-Class参数(Can-Redefine-Classes和Can-Retransform-Classes)
3、将agent类和MF打包
4、正常启动java测试程序
5、使用一个带main方法的java程序attach到java测试程序vm的id,将agent.jar加载到vm里
Demo:
hello.bat、Hello.java、hello.MF用来生成测试jar;
myagent.bat、MyAgent.java、myagent.MF和agentcls文件夹用来生成agent的jar;
startAttach.bat、StartAttach.jar用来将agent.jar加载到目标vm里;
Hello.java代码:
package test;
public class Hello {
public static void main(String[] args) {
System.out.println("HELLO");
Count c = new Count();
while(true){
System.out.println(c.count(1));
try {
Thread.sleep(3000);
}catch(Exception e) {
}
}
}
}
class Count{
public int count(int x){
return x + 1;
};
};
hello.MF文件:
Manifest-Version: 1.0
Created-By: FlyLikeButterfly
Main-Class: test.Hello
hello.bat脚本:
javac -d hellocls Hello.java
jar cvfm hello.jar hello.MF -C hellocls .
pause
MyAgent.java代码:
package test;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class MyAgent {
public static void agentmain(String args, Instrumentation instrumentation) {
Class<?>[] clses = instrumentation.getAllLoadedClasses();
for(Class<?> cls : clses) {
if(cls.getName().equals("test.Count")) {
System.out.println("----" + cls.getName());
for(Method m : cls.getMethods()) {
System.out.println("-" + m);
}
System.out.println("----");
try {
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(cls);
pool.insertClassPath(classPath);
CtClass ccls = pool.get("test.Count");
System.out.println(ccls);
CtMethod cm = ccls.getDeclaredMethod("count");
System.out.println(cm);
cm.setBody("{return $1 * 10;}");
ClassDefinition definitions = new ClassDefinition(cls, ccls.toBytecode());
instrumentation.redefineClasses(definitions);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void agentmain(String args) {
System.out.println("agentmain.");
}
}
myagent.MF文件:
Manifest-Version: 1.0
Created-By: FlyLikeButterfly
Agent-Class: test.MyAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
myagent.bat脚本:
javac -cp agentcls -d agentcls MyAgent.java
jar cvfm myagent.jar myagent.MF -C agentcls .
pause
StartAttach.java代码:
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
public class StartAttach {
public static void main(String[] args) {
for(VirtualMachineDescriptor v : VirtualMachine.list()) {
System.out.println(v.id() + ":" + v.displayName());
if(v.displayName().contains("hello")) {
try {
System.out.println("find hello VM and attach. VM_id=" + v.id());
VirtualMachine vm = VirtualMachine.attach(v.id());
vm.loadAgent("myagent.jar");
vm.startLocalManagementAgent();
vm.detach();
System.out.println("detached VM.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
startAttach.bat脚本:
javac StartAttach.java
java StartAttach
pause
执行测试:
先执行hello.bat和myagent.bat生成测试jar包和代理jar包
先正常执行测试jar包:
java -jar hello.jar
然后执行startAttach.bat脚本将代理jar加载到测试vm里:
另外,执行attach的java程序可以跟agent合成到一个jar里:
将main方法挪到myagent.java里,并且修改myagent.MF文件添加Main-Class
MyAgent.java代码:
package test;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
public class MyAgent {
public static void main(String[] args) {
for(VirtualMachineDescriptor v : VirtualMachine.list()) {
System.out.println(v.id() + ":" + v.displayName());
if(v.displayName().contains("hello")) {
try {
System.out.println("find hello VM and attach. VM_id=" + v.id());
VirtualMachine vm = VirtualMachine.attach(v.id());
vm.loadAgent("myagent.jar");
vm.startLocalManagementAgent();
vm.detach();
System.out.println("detached VM.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void agentmain(String args, Instrumentation instrumentation) {
Class<?>[] clses = instrumentation.getAllLoadedClasses();
for(Class<?> cls : clses) {
if(cls.getName().equals("test.Count")) {
System.out.println("----" + cls.getName());
for(Method m : cls.getMethods()) {
System.out.println("-" + m);
}
System.out.println("----");
try {
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(cls);
pool.insertClassPath(classPath);
CtClass ccls = pool.get("test.Count");
System.out.println(ccls);
CtMethod cm = ccls.getDeclaredMethod("count");
System.out.println(cm);
cm.setBody("{return $1 * 10;}");
ClassDefinition definitions = new ClassDefinition(cls, ccls.toBytecode());
instrumentation.redefineClasses(definitions);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void agentmain(String args) {
System.out.println("agentmain.");
}
}
myagnet.MF文件:
Manifest-Version: 1.0
Created-By: FlyLikeButterfly
Main-Class: test.MyAgent
Agent-Class: test.MyAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
执行hello.bat和myagent.bat生成jar包
命令行分别执行测试jar和代理jar:
修改类最好使用原来加载的类修改,不然很容易出现异常(修饰词不一致,方法名不一致,参数返回值不一致等等)
测试使用attach的时候javassist找不到Object,加了pool.insertClassPath(classPath)解决;
参考:https://www.cnblogs.com/rickiyang/p/11368932.html
更多推荐
所有评论(0)