前言
- 目的:不改造原有项目代码,通过字节码增强技术来实现方法调用耗时
- 学习:最近学习Skywalking时偶然接触到有关字节码方面的知识,特地记录一下(还可以通过javassist来实现)
- 参考资料:
【黑马Skywalking】: java基础教程深入学习Skywalking全套教程
目录结构
环境准备
1. java-agent-demo项目pom:
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--解决字节码操作和Instrumentation API的复杂性-->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<!--打包时加入依赖-->
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<!--自动生成/META-INF/MANIFEST.MF-->
<manifestEntries>
<Premain-Class>PreMainAgent</Premain-Class>
<Agent-Class>PreMainAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
2. PreMainAgentl类:
注意:测试项目要以com.agent前缀开头才能被统计
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
public class PreMainAgent {
public static void premain(String agentParam, Instrumentation inst) {
System.out.println("premain已经执行1" + agentParam);
// method指定哪些方法需要被拦截,ElementMathers.any指定了所有的方法,声明intercept拦截器
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule javaModule) {
return builder.method(ElementMatchers.<MethodDescription>any())
.intercept(MethodDelegation.to(MyIntercept.class));
}
};
/**
* 1.type指定了agent拦截的包名,以[com.agent]作为前缀
* 2.指定了转换器transformer
* 3.将配置安装到Instrumentation
*/
new AgentBuilder.Default()
.type(ElementMatchers.<TypeDescription>nameStartsWith("com.agent"))
.transform(transformer)
.installOn(inst);
}
public static void premain(String agentParam) {
System.out.println("premain已经执行2" + agentParam);
}
}
3. MyIntercept类:
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class MyIntercept {
@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 + ":" + (System.currentTimeMillis() - start) + "ms");
}
}
}
4. Main类:
注意:测试项目要以com.agent前缀开头才能被统计
public class Main {
public static void main(String[] args) throws InterruptedException {
System.out.println("main方法执行");
Thread.sleep(1000);
}
}
测试结果
1. java-agent-demo打jar包
注意:记住java-agent-demo的jar包路径
2. 测试项目启动参数
在测试项目中的启动VM options添加一下参数,我自己随便用的参数:guantest
-javaagent:D:\project4\java-agent-demo\target\java-agent-demo-1.0-SNAPSHOT.jar=guantest
3. 测试结果