摘要:在 SkyWalking 课程中有一小节讲到了 Java Agent,零代码侵入就能实现 JVM 层面的 AOP 增强的好技术。
介绍:
使用步骤:
举个栗子
agent-demo 工程
必须提供的 premain() 方法。
public class AgentDemo {
public static void premain(String agentArgs, Instrumentation inst) throws Exception {
System.out.println("premain(String agentArgs, Instrumentation inst)");
System.out.println("参数:" + agentArgs);
System.out.println("参数:" + inst);
}
public static void premain(String agentArgs) throws Exception {
System.out.println("premain(String agentArgs)");
System.out.println("参数:" + agentArgs);
}
}
agent-demo 工程编译成 agent-demo.jar 之后。打开压缩包修改 MANIFEST.MF 文件。
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: chenxinjie
Build-Jdk: 13.0.2
Specification-Title: agent-demo
Specification-Version: 0.0.1-SNAPSHOT
Implementation-Title: agent-demo
Implementation-Version: 0.0.1-SNAPSHOT
Implementation-Vendor-Id: cn.live
Can-Retransform-Classes: true
Premain-Class: cn.live.AgentDemo
main-demo 工程
写一个简单的 Java Application 方法,在命令行输出一段字符串。
public class MainDemo {
public static void main(String[] args) throws Exception {
System.out.println("MainDemo end");
}
}
直接运行 Java Application,得到的结果是:
MainDemo end
当修改 VM arguments 参数,加上`-javaagent:"/Users/chenxinjie/GitHub/springboot-demo/agent-demo/target/agent-demo.jar=systemNo=main-demo"` 运行 Java Application,得到的结果是:
premain(String agentArgs, Instrumentation inst)
参数:systemNo=main-demo
参数:sun.instrument.InstrumentationImpl@5caf905d
MainDemo end
统计方法执行时间
public java.lang.String cn.live.service.impl.DemoServiceImpl.hello(java.lang.String) hook :1004ms
public java.lang.String cn.live.controller.DemoController.hello() hook :3004ms
以上是 Java Agent 技术与 bytebuddy 工具,配合实现的统计方法执行时间功能。
agent-demo 工程
修改 premain() 方法,使用 bytebuddy 工具动态生产 Agent。
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
@Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1, ClassLoader arg2,
JavaModule arg3) {
return arg0.method(ElementMatchers.any())
.intercept(MethodDelegation.to(TimeInterceptor.class));
}
};
new AgentBuilder.Default().type(ElementMatchers.nameStartsWith("cn.live"))
.transform(transformer).installOn(inst);
新增 `TimeInterceptor.java` 类。
@RuntimeType
public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable) throws Exception {
long start = System.currentTimeMillis();
try {
return callable.call(); // 执行方法
} finally {
System.out.println(method + " hook :" + (System.currentTimeMillis() - start) + "ms");
}
}
小结
感到万分尴尬,如此优秀的 Java Agent 从来都没有关注过!
本文主要通过两个案例来了解 Java Agent 技术,顺便提出两个问题:Java Agent、bytebuddy 到底是怎么样的一个实现原理?有什么优秀的开源案例?