java agent 实现方法监控
首先先看一段main方法
public class Main {
public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
List<VirtualMachineDescriptor> listAfter = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : listAfter) {
if (vmd.displayName().equals("com.dfire.soa.report.utils.Main")) {
VirtualMachine attach = VirtualMachine.attach(vmd.id());
attach.loadAgent("C:\\Users\\qinghao\\Desktop\\Java\\agent.jar");
attach.detach();
break;
}
}
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Target target = new Target();
target.doBusiness("dsad", "hndsjadsaa");
}
}
1 其中 VirtualMachine.list() 用于获取当前系统中所有的java进程信息 由于我是要对Target.doBusiness 方法进行监控 所以 displayName == com.dfire.soa.report.utils.Main 即可 ( 注: VirtualMachine.list() 与 jps -mlvV 获取到的信息大致相同 感兴趣可以去看看 , 2种方式调用的底层应该是一样的 )
2 找到对应vmd.id() 这个便是当前main方法运行的进程ID
3 loadAgent 必须要打成jar的方式 注( 里面只有1个文件 ) 注意里面红色的部分 agent-class 是你需要attach 的类
Can-Retransform-Classess 这个属性非常的重要 否则会报错
4 detach
以上便完成了 虚拟机进程的attach
按照顺序 agent-class 该类是代理类 也就是jar -> MANIFEST.MF指定的文件
要实现虚拟机动态agent 需要代理类实现如下2个方法
一下2个方法是在 java.lang.ClassLoader#defineClass1之前被调用的 如图
agentmain(String agentArgs, Instrumentation inst) inst可以addTransformer来实现对虚拟机load clazz byte 的修改
agentmain(String agentArgs)
public class QingHao {
public static void agentmain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException,
InterruptedException {
inst.addTransformer(new AgentClass(), true);
inst.retransformClasses(AgentClass.class);
System.out.println("Agent Main Done");
}
}
本次实现用的是javassist 处理字节码
大致思路将 Target 中doBusiness 方法重新封装一下 (Target类会在后面给出)
- 将doBusiness的方法体拷贝到doBuesiness$Impl
- 重新生成一个新doBusiness 的方法
- 在新doBusiness方法中实现对参数 a,b 以及返回TargetReturn 对象的监控
public class AgentClass implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println(loader);
System.out.println(className);
System.out.println(classBeingRedefined);
System.out.println(protectionDomain);
System.out.println(classfileBuffer);
if ("com/dfire/soa/report/utils/Target".equals(className)) {
System.out.println(" Target --->>dsjadsads a");
try {
CtClass ctClass = ClassPool.getDefault().getCtClass("com.dfire.soa.report.utils.Target");
System.out.println(ctClass);
CtMethod ctMethod = ctClass.getDeclaredMethod("doBusiness");
System.out.println(ctMethod);
String name = ctMethod.getReturnType().getName();
//拷贝一个方法
CtMethod copy = CtNewMethod.copy(ctMethod ,"doBusiness", ctClass, null);
System.out.println(copy.getName());
String oldMethodName = "doBuesiness$Impl";
ctMethod.setName(oldMethodName);
// 对输入参数的监控
ctMethod.insertBefore(("System.out.println(\"a 参数\" + a) ; System.out.println( \"b 参数\"+b) ; "));
System.out.println(ctMethod.getName());
System.out.println( "----------->" );
String st = ("{") ;
st += (name + " xx = null; " );
st += ("xx = " + oldMethodName+"($$);");
st += ("System.out.println( \" 执行结果\" + xx );");
st += ("return xx;");// 返回的对象
st += ("}");
copy.setBody(st);
ctClass.addMethod(copy);
return ctClass.toBytecode();
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// if ("com.dfire.soa.report.utils.Target".equals(className)) {
// ClassPool pool = ClassPool.getDefault();
// CtClass cc = null;
// try {
// cc = pool.get("com.dfire.soa.report.utils.Target");
// CtMethod cm = cc.getDeclaredMethod( "doBusiness" );
//
//
cm.insertBefore( );
// } catch (NotFoundException e) {
// e.printStackTrace();
// }
// }
// return null 从原来class池里面取数据
return null;
}
}
上面类所用到的Target
public class Target {
public TargetReturn doBusiness( String a, String b ) {
TargetReturn targetReturn = new TargetReturn();
targetReturn.setA(a);
targetReturn.setB(b);
return targetReturn;
}
}
public class TargetReturn {
private String a ;
private String b ;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
然后看最后的运行结果
[B@71a7217b
Target --->>dsjadsads a
javassist.CtClassType@3003596d[public class com.dfire.soa.report.utils.Target fields= constructors=javassist.CtConstructor@4eae0e13[public Target ()V], methods=javassist.CtMethod@4a2d9118[public doBusiness (Ljava/lang/String;Ljava/lang/String;)Lcom/dfire/soa/report/utils/TargetReturn;], ]
javassist.CtMethod@4a2d9118[public doBusiness (Ljava/lang/String;Ljava/lang/String;)Lcom/dfire/soa/report/utils/TargetReturn;]
doBusiness
doBuesiness$Impl
----------->
a 参数dsad
b 参数hndsjadsaa
sun.misc.Launcher$AppClassLoader@18b4aac2
com/dfire/soa/report/utils/TargetReturn
null
ProtectionDomain (file:/D:/WorkSpaces/new_retail/retail-report-soa/retail-report-client/target/classes/ <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@2c95d52c (
("java.io.FilePermission" "\D:\WorkSpaces\new_retail\retail-report-soa\retail-report-client\target\classes\-" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@49d733db
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringBuilder
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@8151e94
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/Builder
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@6c32a70c
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringStyle
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@4bf0fc83
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringStyle$DefaultToStringStyle
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@79ab212e
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringStyle$MultiLineToStringStyle
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@76dd6693
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringStyle$NoFieldNameToStringStyle
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@2f50f8b0
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringStyle$ShortPrefixToStringStyle
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@445d0c07
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringStyle$SimpleToStringStyle
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@23196e9b
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringStyle$NoClassNameToStringStyle
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@1b2bab38
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringStyle$JsonToStringStyle
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@139c3a05
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ReflectionToStringBuilder
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@21ba4b9c
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/Validate
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@61ba30b6
null
java/lang/IllegalStateException
null
null
[B@437e6a0a
sun.misc.Launcher$AppClassLoader@18b4aac2
org/apache/commons/lang3/builder/ToStringExclude
null
ProtectionDomain (file:/D:/Storage/m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar <no signer certificates>)
sun.misc.Launcher$AppClassLoader@18b4aac2
<no principals>
java.security.Permissions@5c44c877 (
("java.io.FilePermission" "\D:\Storage\m2\repository\org\apache\commons\commons-lang3\3.7\commons-lang3-3.7.jar" "read")
("java.lang.RuntimePermission" "exitVM")
)
[B@55ad91c7
null
sun/reflect/annotation/AnnotationParser
null
null
[B@18774e30
null
sun/reflect/UnsafeObjectFieldAccessorImpl
null
null
[B@3513e002
执行结果com.dfire.soa.report.utils.TargetReturn@61df0988[a=dsad,b=hndsjadsaa]
从执行结果来看有很多类的信息输出
总结
优点
- 该实现可以动态agent 无需用jar -javaagent 的方式
- 该实现对项目中编写的类无侵入性
缺点
- Target 应该缓存起来
- agent.jar 中manifest.mf的指定必须要以jar的形式指定
感谢小伙伴提供许多的文章 参考如下
http://blog.csdn.net/mousebaby808/article/details/37696371