CGLIB在没有接口的时候,已经可以完美的代理父类方法了。在运行的时候抛出module限制的异常。提示违反了"java.lang"模块的安全限制。因此找了下当前是否有更新的动态代理框架?找到了ByteBuddy。
ByteBuddy 是一个强大的 Java 字节码操作库,它允许开发者在运行时动态地创建、修改和操作 Java 类。它类似于其他字节码操作工具(如 ASM 或 CGLIB),但提供了更高级的抽象和更易用的 API。ByteBuddy 广泛应用于 AOP(面向切面编程)、动态代理、插件系统、测试框架等领域。
ByteBuddy 的核心特点
-
高级抽象:ByteBuddy 提供了高级抽象,使得字节码操作更加直观和易于理解。它隐藏了底层字节码操作的复杂性,让开发者可以更专注于业务逻辑。
-
灵活性:支持在运行时动态创建和修改类,可以轻松实现 AOP、动态代理等功能。
-
与 Java 代理的兼容性:ByteBuddy 支持与 Java 的
java.lang.reflect.Proxy
和 CGLIB 等代理机制无缝集成。 -
性能优化:ByteBuddy 生成的字节码经过优化,运行效率高,适合在生产环境中使用。
-
丰富的 API:提供了丰富的 API,支持各种复杂的字节码操作,包括方法拦截、字段注入、类继承等。
1 动态代理有了CGLIB,为什么还需要ByteBuddy呢?
CGLIB 和 ByteBuddy 都是用于动态代理的工具,但它们在设计理念、功能和使用场景上存在一些差异。以下是为什么需要 ByteBuddy 的几个主要原因:
1.1. 更灵活的API
CGLIB:API相对简单,主要基于字节码操作,适合基本的类增强需求。
ByteBuddy:提供了更丰富和灵活的API,允许开发者以更直观的方式定义和修改类结构。它不仅支持生成子类,还支持接口实现、方法拦截、字段注入等复杂操作。
1.2. 更高的抽象层次
CGLIB:直接基于ASM(一个低级别的字节码操作库)实现,开发者需要对字节码有一定了解。
ByteBuddy:封装了底层字节码操作细节,提供更高层次的抽象,使得开发者可以专注于业务逻辑,而不需要深入了解字节码。
1.3. 更好的性能优化
在某些场景下,ByteBuddy 的性能表现优于 CGLIB。例如,ByteBuddy 支持更细粒度的控制和优化选项,能够生成更加高效的代理类。
1.4. 支持更多高级特性
ByteBuddy 提供了更多的高级特性,比如:
方法重写
字段注入
注解处理
动态类型生成
这些特性使得 ByteBuddy 更适合复杂的动态代理场景。
1.5. 社区活跃性和维护性
ByteBuddy 的社区活跃度较高,更新频率快,能够及时修复已知问题并引入新特性。
相比之下,CGLIB 的更新较慢,部分功能可能无法满足最新的开发需求。
2. 使用ByteBuddy实现动态代理?
还是前面计算器的例子,怎样通过ByteBuddy来实现动态代理呢?
2.1添加 ByteBuddy 依赖
确保你的项目中包含了 ByteBuddy 的依赖。
如果你使用的是 Maven,可以在 pom.xml 中添加以下依赖:
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.6</version>
</dependency>
2.2 定义方法拦截类
public class ByteBuddyMethodInterceptor {
private Object realObject;
public ByteBuddyMethodInterceptor(Object realObject) {
this.realObject = realObject;
}
@RuntimeType
public Object intercept(@This Object obj, @Origin Method method, @AllArguments Object[] args) {
Object result = null;
try {
System.out.println("Before ByteBuddyMethodInterceptor: " + method.getName());
long start = System.currentTimeMillis();
result = method.invoke(realObject, args);
long end = System.currentTimeMillis();
System.out.println("Cost time[" + (end - start) + "]ms");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
功能:这是主要的拦截逻辑方法,负责拦截目标方法的调用并执行自定义逻辑。
参数说明:
@This Object obj:表示当前代理对象。
@Origin Method method:表示被拦截的方法。
@AllArguments Object[] args:表示被拦截方法的参数列表。
这里可以不用实现某个固定的InvocationHandler或者MethodInterceptor类似的接口了,ByteBuddy自己自动匹配拦截。
2.3 生成代理类
public class ByteBuddyProxyDemo {
public static void main(String[] args) throws Exception {
// 创建 BusinessCalculator 的实例
BusinessCalculator realObject = new BusinessCalculator();
// 使用 ByteBuddy 创建动态代理
DynamicType.Unloaded<BusinessCalculator> dynamicType = new ByteBuddy()
.subclass(BusinessCalculator.class)
.method(ElementMatchers.any())
.intercept(MethodDelegation.to(new ByteBuddyMethodInterceptor(realObject)))
.make();
// 加载动态代理类
Class<? extends BusinessCalculator> proxyClass = dynamicType.load(ByteBuddyProxyDemo.class.getClassLoader())
.getLoaded();
// 创建代理实例
BusinessCalculator proxyInstance = proxyClass.getDeclaredConstructor().newInstance();
// 调用代理方法
System.out.println("Add Result: " + proxyInstance.add(5, 3));
System.out.println("Subtract Result: " + proxyInstance.subtract(5, 3));
System.out.println("Multiply Result: " + proxyInstance.multiply(5, 3));
System.out.println("Divide Result: " + proxyInstance.divide(5, 3));
}
}
通过 ByteBuddy 的 API 生成 BusinessCalculator 的子类,并将拦截逻辑注入到子类的方法中。
关键步骤:
生成子类:使用 subclass(BusinessCalculator.class) 生成 BusinessCalculator 的子类。
匹配方法:使用 method(ElementMatchers.any()) 匹配 BusinessCalculator 类中的所有方法。
拦截方法:使用 intercept(MethodDelegation.to(new ByteBuddyMethodInterceptor(realObject))) 将方法调用委托给 ByteBuddyMethodInterceptor。
使用 ByteBuddy 的 API 生成代理类,并将拦截器逻辑注入到目标类的方法中。
2.4 运行结果
Before ByteBuddyMethodInterceptor: add
Cost time[31]ms
Add Result: 5000000750000000
Before ByteBuddyMethodInterceptor: subtract
Cost time[30]ms
Subtract Result: 5000000150000000
Before ByteBuddyMethodInterceptor: multiply
Cost time[31]ms
Multiply Result: 5000001450000000
Before ByteBuddyMethodInterceptor: divide
Cost time[42]ms
Divide Result: 5000000050000000
3 ByteBuddy 动态代理原理
3.1 保存生成的字节码
// 使用 ByteBuddy 创建动态代理
DynamicType.Unloaded<BusinessCalculator> dynamicType = new ByteBuddy()
.subclass(BusinessCalculator.class)
.method(ElementMatchers.any())
.intercept(MethodDelegation.to(new ByteBuddyMethodInterceptor(realObject)))
.make();
// 保存生成的类文件到指定目录
dynamicType.saveIn(new File("net\\bytebuddy"));
// 加载动态代理类
Class<? extends BusinessCalculator> proxyClass = dynamicType.load(ByteBuddyProxyDemo.class.getClassLoader())
.getLoaded();
在构造完成代理类后,我们新增了一句:
// 保存生成的类文件到指定目录
dynamicType.saveIn(new File("net\\bytebuddy"));
即可把生成的代理类源文件,保存在当前工程的net/bytebuddy目录下。
3.2 查看代理类源文件
代理类源文件如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.derek;
public class BusinessCalculator$ByteBuddy$G91R9HI3 extends BusinessCalculator {
public boolean equals(Object var1) {
return (Boolean)delegate$9023om1.intercept(this, cachedValue$aLU1OcVA$5j4bem0, new Object[]{var1});
}
public String toString() {
return (String)delegate$9023om1.intercept(this, cachedValue$aLU1OcVA$4cscpe1, new Object[0]);
}
public int hashCode() {
return (Integer)delegate$9023om1.intercept(this, cachedValue$aLU1OcVA$9pqdof1, new Object[0]);
}
protected Object clone() throws CloneNotSupportedException {
return delegate$9023om1.intercept(this, cachedValue$aLU1OcVA$7m9oaq0, new Object[0]);
}
public Long add(Integer var1, Integer var2) {
return (Long)delegate$9023om1.intercept(this, cachedValue$aLU1OcVA$uqp0va0, new Object[]{var1, var2});
}
public Long multiply(Integer var1, Integer var2) {
return (Long)delegate$9023om1.intercept(this, cachedValue$aLU1OcVA$11g4td1, new Object[]{var1, var2});
}
public Long divide(Integer var1, Integer var2) {
return (Long)delegate$9023om1.intercept(this, cachedValue$aLU1OcVA$m9l12j0, new Object[]{var1, var2});
}
public Long subtract(Integer var1, Integer var2) {
return (Long)delegate$9023om1.intercept(this, cachedValue$aLU1OcVA$h8e6ka0, new Object[]{var1, var2});
}
public BusinessCalculator$ByteBuddy$G91R9HI3() {
}
}
ByteBuddy 通过字节码操作生成一个新的类,这个类是 BusinessCalculator 的子类,并在子类的方法中插入拦截逻辑。具体步骤如下:
生成子类:创建一个新的类,继承自 BusinessCalculator。
重写方法:在子类中重写 BusinessCalculator 的所有方法。
插入拦截逻辑:在每个方法中插入拦截器逻辑,即调用 ByteBuddyMethodInterceptor 的 intercept 方法。
方法调用:通过代理实例调用方法时,实际调用的是拦截器中的逻辑。
参考文章:
Byte Buddy - runtime code generation for the Java virtual machine