ByteBuddy官方文档
https://bytebuddy.net/#/tutorial-cn
反编译类网址
ByteBuddy之Javaagent使用,实现类加载拦截,修改类的字节码,实现一些业务逻辑。例如采集方法耗时…
ByteBuddy之Javaagent使用
添加bytebuddy的依赖:
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.12.13</version>
</dependency>
方法代理
方法代理:类似与SpringAOP,创建一个代理对象,去调用目标方法。bytebuddy的方法代理是在目标方法中调用代理类的方法,然后再回调的本类的源方法。
代码演示
public class UserService {
public String selectUser(String username) {
return "张三";
}
}
public class AgentMain {
public static void premain(String agentArgs, Instrumentation instrumentation) {
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) {
// 增强方法{selectUser}
return builder.method(ElementMatchers.named("selectUser"))
// 设置拦截器
.intercept(MethodDelegation.to(AgentMain.Interceptor.class));
}
};
new AgentBuilder.Default()
// 忽略掉不增强的类
.ignore(nameStartsWith("net.bytebuddy."))
// 增强的类
.type(ElementMatchers.named("com.liuqi.service.UserService"))
// 增强的类需 增强的方法实现
.transform(transformer)
// 注册增强类监听器
.with(new AgentBuilder.Listener() {
public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {}
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
String className = typeDescription.getName();
System.out.println("增强类: " + className);
}
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {}
public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
System.out.println("增强类失败: " + typeName + "; 失败原因: " + throwable);
}
public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) { }
})
// 监听类加载
.installOn(instrumentation);
// 增强后的类, 写入至文件, 便于观察
instrumentation.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (className.contains("UserService")) {
try {
String substring = className.substring(className.lastIndexOf("/"));
Files.write(new File("C:/Users/86187/Desktop/bytebuddy-class/" + substring + ".class").toPath(), classfileBuffer);
} catch (IOException e) {
e.printStackTrace();
}
}
return classfileBuffer;
}
});
}
public static class Interceptor {
@RuntimeType
public static Object intercept(@This Object target, // 当前拦截的目标对象{this}
@AllArguments Object[] allArguments, // 方法入参
@SuperCall Callable<?> superCall, // 代理对象
@Origin Method method // 当前拦截的目标方法
) throws Throwable {
Object result = null; // 方法返回结果
try {
System.out.println("前置拦截");
// 执行目标方法
result = superCall.call();
// 返回目标方法执行结果
return result;
} catch (Throwable t) {
System.out.println("异常拦截");
throw t;
} finally {
System.out.println("后置拦截: 方法返回值 = " + result);
}
}
}
}
运行效果
查看增强后的类
可以看到bytebuddy
为UserService类又生成了一个类。
反编译 UserService
类
标注synthetic
说明是编译器生成的。
package com.liuqi.service;
import com.liuqi.agent.AgentMain.Interceptor;
import com.liuqi.service.UserService.auxiliary.VdebFR3h;
import java.lang.reflect.Method;
public class UserService {
// $FF: synthetic field
// 声明方法字段
private static final Method cachedValue$wCBkyXnP$fi9hdm2;
// 源目标方法, 字节码已被修改, 调用逻辑为:调用拦截的方法
public String selectUser(String var1) {
// new VdebFR3h(this, var1) 创建代理对象
return (String)Interceptor.intercept(
this, // 当前实例对象
new Object[]{var1}, // 方法入参
new VdebFR3h(this, var1), // 代理对象
cachedValue$wCBkyXnP$fi9hdm2 // 拦截方法
);
}
// $FF: synthetic method
// 源目标方法被重命名,
private String selectUser$original$uTSGTdQZ(String username) {
return "张三";
}
// 静态初始化代码块, bytebuddy生成的,
static {
ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.dynamic.Nexus").getMethod("initialize", new Class[]{Class.class, Integer.TYPE}).invoke((Object)null, new Object[]{UserService.class, Integer.valueOf(-449407519)});
// // 为字段 {Method: cachedValue$wCBkyXnP$fi9hdm2} 赋值
cachedValue$wCBkyXnP$fi9hdm2 = UserService.class.getMethod("selectUser", new Class[]{String.class});
}
// $FF: synthetic method
// 为代理类提供的调用方法, 取调用目标方法.
// 因为目标方法被修饰了{private}, 非本类不能访问.
final String selectUser$original$uTSGTdQZ$accessor$wCBkyXnP(String var1) {
return this.selectUser$original$uTSGTdQZ(var1);
}
}
反编译 UserService
a
u
x
i
l
i
a
r
y
auxiliary
auxiliaryVdebFR3h
类
package com.liuqi.service;
import com.liuqi.service.UserService;
import java.util.concurrent.Callable;
// $FF: synthetic class
class UserService$auxiliary$VdebFR3h implements Runnable, Callable {
// 目标方法实例
private UserService argument0;
// 目标方法入参
private String argument1;
// 调用目标方法
public Object call() throws Exception {
return this.argument0.selectUser$original$uTSGTdQZ$accessor$wCBkyXnP(this.argument1);
}
// 调用目标方法, 无返回值
public void run() {
this.argument0.selectUser$original$uTSGTdQZ$accessor$wCBkyXnP(this.argument1);
}
// 构造函数
UserService$auxiliary$VdebFR3h(UserService var1, String var2) {
this.argument0 = var1;
this.argument1 = var2;
}
}
方法代理 - 可修改入参
自定义一个类。
public interface MyCallable {
/**
* 定义一个方法, 方法名随意 {此类中只能有这一个方法, 只能有一个入参}
*
* @param args 方法入参
* @return
*/
Object call(Object[] args);
}
在设置拦截器时,写入以下配置:
MethodDelegation.withDefaultConfiguration().withBinders(Morph.Binder.install(MyCallable.class))
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) {
// 增强方法{selectUser}
return builder.method(ElementMatchers.named("selectUser"))
// 设置拦截器
.intercept(MethodDelegation.withDefaultConfiguration() // 写入某些配置
.withBinders(Morph.Binder.install(MyCallable.class)) // 设置一个代理调用类
.to(AgentMain.Interceptor.class));
}
};
拦截器修改以下配置:
使用@Morph MyCallable
接收代理对象
public static class Interceptor {
@RuntimeType
public static Object intercept(@This Object target, // 当前拦截的目标对象{this}
@AllArguments Object[] allArguments, // 方法入参
@Origin Method method, // 当前拦截的目标方法
@Morph MyCallable superCall // 代理对象
) throws Throwable {
Object result = null; // 方法返回结果
try {
System.out.println("前置拦截");
// 修改参数
allArguments[0] = "1111";
// 执行目标方法
result = superCall.call(allArguments);
// 返回目标方法执行结果
return result;
} catch (Throwable t) {
System.out.println("异常拦截");
throw t;
} finally {
System.out.println("后置拦截: 方法返回值 = " + result);
}
}
}
方法字节码修改
创建拦截器
package com.liuqi.agent;
import net.bytebuddy.asm.Advice;
import java.lang.reflect.Method;
/**
* @author liuqi
* @date 2022/12/15 12:27
* @since 1.0.0
**/
public class AdviceInterceptor {
/**
* 方法运行前: 执行代码的片段, 必须是静态的方法
*/
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void enter(@Advice.This Object that, // 当前实例
@Advice.AllArguments Object[] args, // 方法入参
@Advice.Origin Method method) // 目标方法
{
System.out.println("前置拦截 ");
}
/**
* 方法退出前, 代码执行片段, 必须是静态的方法
*/
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void exit(@Advice.This Object that, // 当前实例
@Advice.AllArguments Object[] args, // 方法入参
@Advice.Origin Method method, // 目标方法
@Advice.Return Object result, // 返回值
@Advice.Thrown Throwable t // 异常, 如果没有异常, 则 = null
)
{
if (t != null) {
System.out.println("异常拦截");
} else {
System.out.println("后置拦截 ");
}
}
}
设置拦截器
在transform
方法中填写new AgentBuilder.Transformer.ForAdvice()
实例。
advice
方法中有两个入参:
- 参数 0:增强方法匹配器
- 参数 1:拦截器类名
new AgentBuilder.Default()
// 忽略掉不增强的类
.ignore(nameStartsWith("net.bytebuddy."))
// 增强的类
.type(ElementMatchers.named("com.liuqi.service.UserService"))
// 增强的类需 增强的方法实现
.transform(
new AgentBuilder.Transformer.ForAdvice()
.advice(ElementMatchers.named("selectUser"), "com.liuqi.agent.AdviceInterceptor")
)
// 监听类加载
.installOn(instrumentation);
查看增强后的类
可以看到bytebuddy
没有生成其他的代理类。
反编译 UserService
类
可以看到,此增强方式,是将拦截器中的代码直接拷贝至此方法中
。
package com.liuqi.service;
public class UserService {
public String selectUser(String var1) {
try {
System.out.println("前置拦截 ");
} catch (Throwable var7) {
;
}
String var2;
Throwable username;
label31: {
String var10000;
try {
var10000 = "张三";
} catch (Throwable var8) {
username = var8;
var2 = null;
break label31;
}
var2 = var10000;
username = null;
}
try {
if(username != null) {
System.out.println("异常拦截");
} else {
System.out.println("后置拦截 ");
}
} catch (Throwable var6) {
;
}
if(username != null) {
throw username;
} else {
return var2;
}
}
static {
ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.dynamic.Nexus").getMethod("initialize", new Class[]{Class.class, Integer.TYPE}).invoke((Object)null, new Object[]{UserService.class, Integer.valueOf(250183171)});
}
}
总结
方法代理
方法代理的方式,会为每个方法增强都会生成一个代理对象。例如一个类中,需要增强两个方法,则会生成两个代理类。
skywalking
是使用此方式。
字节码方法修改
此方式只是将代理类的执行代码拷贝至目标方法中,并不会生成额外的代理类。即使一个类中增强了多个方法。
OpenTelemetry
使用此方式。