Java代理(五)动态代理之ByteBuddy

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

Java字节码增强库ByteBuddy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值