1、byteBuddy介绍
官网教程:http://bytebuddy.net/#/tutorial
中文翻译教程:https://notes.diguage.com/byte-buddy-tutorial/#creating-java-agents
Byte Buddy是一个代码生成和操作库,用于在Java应用程序的运行时期间创建和修改Java类,而无需编译器的帮助。除了Java类库附带的代码生成实用程序之外,Byte Buddy允许创建任意类,并且不限于实现用于创建运行时代理的接口。此外,Byte Buddy提供了一个方便的API,可以手动,使用Java代理或在构建期间更改类。
官网对其他字节码框架进行了点评:Java proxies 、CGLIB、Javassist,有兴趣的可以了解下,看看英文,不懂就google翻译一下,不外乎就是吹吹自己,找找其他框架的问题。我们引入byteBuddy主要要解决的问题就是和javaagent的Instrumentation衔接,更加方便的对我们需要加强的类进行拦截和加强,这也是调用链采集框架的核心所在。
下面基于前面002章节搭建好的框架,引入byteBuddy,实现一个对类的实例方法进行拦截和加强的DEMO。
2、项目扩展
项目结构:
DEMO说明:
SnifferAgent实现对TestByteBuddy的print方法进行拦截加强。
TestSnifferAgent调用TestByteBuddy里面的两个方法:print和printWithoutInterceptor,测试SnifferAgent的拦截效果。
关键代码:
gy4j-monitor-sniffer的pom.xml
引入byteBuddy的依赖
net.bytebuddy
byte-buddy
${bytebuddy.version}
SnifferAgent.java:
public class SnifferAgent {
/**
* 在方法在main方法之前执行,和main方法同Jvm、ClassLoader、Security policy和Context
*
* @param agentOps javaagent入参
* @param inst 对class进行字节码加强的代理实例
*/
public static void premain(String agentOps, Instrumentation inst) {
System.out.println("hello javaagent!this is premain!");
// 基于ByteBuddy建立agent规则
final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.ENABLED);
new AgentBuilder.Default(byteBuddy)
// 忽略不需要拦截的类
.ignore(initIgnoreElementMatcher())
// 对类名为cn.gy4j.monitor.test.sniffer.agent.TestByteBuddy的类进行拦截
.type(ElementMatchers.named("cn.gy4j.monitor.test.sniffer.agent.TestByteBuddy"))
// 对拦截类进行加强
.transform(new AgentTransformer())
// 类加强的侦听器:类加强过程中的事件侦听
.with(new AgentListener())
// 基于inst
.installOn(inst);
}
/**
* 忽略规则构建
*
* @return
*/
private static ElementMatcher initIgnoreElementMatcher() {
// synthetic总的来说,是由编译器引入的字段、方法、类或其他结构,主要用于JVM内部使用
// 参考:https://blog.csdn.net/a327369238/article/details/52608805
return nameStartsWith("net.bytebuddy.").or(ElementMatchers.isSynthetic());
}
/**
* 转换规则构建
*/
static class AgentTransformer implements AgentBuilder.Transformer {
@Override
public DynamicType.Builder> transform(DynamicType.Builder> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
// 对拦截类的print方法进行拦截加强,加强的规则为TestByteBuddyPrintInterceptor
return builder.method(ElementMatchers.named("print"))
.intercept(MethodDelegation.withDefaultConfiguration().to(new TestByteBuddyPrintInterceptor()));
}
}
/**
* 侦听器
*/
static class AgentListener implements AgentBuilder.Listener {
@Override
public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
}
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
System.out.println("onTransformation:" + typeDescription);
}
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {
}
@Override
public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
throwable.printStackTrace();
System.out.println("onError:" + typeName);
}
@Override
public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
}
}
}
TestByteBuddyPrintInterceptor.java
public class TestByteBuddyPrintInterceptor {
@RuntimeType
public Object intercept(@This Object obj,
@AllArguments Object[] allArguments,
@SuperCall Callable> zuper,
@Origin Method method) throws Throwable {
try {
// 原方法执行前
System.out.println(" before method:" + method.getName());
} catch (Throwable t) {
t.printStackTrace();
System.out.println("class[" + obj.getClass() + "] before method[" + method.getName() + "] intercept failure");
}
Object ret = null;
try {
// 原方法调用
ret = zuper.call();
} catch (Throwable t) {
try {
// 原方法调用异常
System.out.println("exception method:" + method.getName());
} catch (Throwable t2) {
System.out.println("class[" + obj.getClass() + "] handle method[" + method.getName() + "] exception failure");
}
throw t;
} finally {
try {
// 原方法执行后
System.out.println("after method:" + method.getName());
} catch (Throwable t) {
System.out.println("class[" + obj.getClass() + "] after method[" + method.getName() + "] intercept failure");
}
}
return ret;
}
}
TestSnifferAgent.java
public class TestSnifferAgent {
/**
* 测试SnifferAgent的main方法
* jvm配置:-javaagent:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar
*
* @param args
*/
public static void main(String[] args) {
System.out.println("hello javaagent!this is main!");
TestByteBuddy testByteBuddy = new TestByteBuddy();
testByteBuddy.print();
testByteBuddy.printWithoutInterceptor();
}
}
TestByteBuddy.java
public class TestByteBuddy {
public void print() {
System.out.println("this is the method:print");
}
public void printWithoutInterceptor() {
System.out.println("this is method:printWithoutInterceptor");
}
}
3、测试验证
第一步:编译打包
mvn clean package
获取路径:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar
第二步:测试验证
加入jvm参数(使用前面copy的jar路径):
-javaagent:G:\gy4j\git_gy4j\gy4j-monitor\gy4j-monitor-sniffer\target\sniffer-agent.jar
执行TestSnifferAgent的main方法:
4、源码地址
https://github.com/gy4j/gy4j-monitor/tree/003-byteBuddy