返回

独家揭秘动态代理两大巨头JDK和CGLIB的奥秘

后端

动态代理:超越传统,赋予代码新功能

在软件开发的浩瀚世界中,我们时常遇到这样的场景:迫切需要为现有代码添加额外功能,却苦于无法直接修改源代码。此时,动态代理技术如同一位隐形英雄,悄然登场,为我们带来了解决之道。

何谓动态代理

动态代理是一种面向切面编程(AOP)的技术,允许我们在不触碰原始代码的情况下,为类或接口添加附加功能。它如同一位幕后操手,在方法调用时悄然介入,执行我们预先定义的功能,而不影响原有代码的正常运作。

JDK动态代理:灵活多变的帮手

Java SE 中的 JDK 动态代理是业界常用的动态代理实现。它利用 Java 反射机制来创建代理对象,专精于代理接口,而非类。

JDK动态代理的工作原理

JDK动态代理的工作流程十分简洁:

  1. 创建一个 InvocationHandler 接口的实现类,负责在方法调用时执行预定义的功能。
  2. 借助 Proxy 类的 newProxyInstance() 方法,传入代理类的类加载器、实现的接口列表和 InvocationHandler 实现,生成代理对象。
  3. 通过代理对象调用目标类或接口的方法时,代理对象会将方法调用转发给 InvocationHandler 的实现类,从而触发预定义功能的执行。

JDK动态代理的优缺点

JDK动态代理的优点不容忽视:

  • 使用简单,只需实现 InvocationHandler 接口即可。
  • 性能优异,代理对象通过运行时动态生成,无需修改字节码。

然而,它的不足也显而易见:

  • 只能代理接口,对于类无能为力。
  • 代理类只能继承自 InvocationHandler 接口,限制了代理功能的扩展。

CGLIB动态代理:万能的魔术师

CGLIB动态代理脱颖而出,因为它既能代理接口,也能代理类。它利用 ASM 字节码生成框架,为代理对象注入新的生命。

CGLIB动态代理的工作原理

CGLIB动态代理的工作机制同样清晰明了:

  1. 创建一个 MethodInterceptor 接口的实现类,在方法调用时负责执行预定义的功能。
  2. 通过 Enhancer 类的 create() 方法,传入目标类的类型和 MethodInterceptor 实现,生成代理对象。
  3. 代理对象接收方法调用时,会将调用转发给 MethodInterceptor 的实现类,进而执行预定义功能。

CGLIB动态代理的优缺点

CGLIB动态代理拥有诸多优势:

  • 代理能力全面,可同时代理接口和类。
  • 代理类可以继承自任意类,扩展功能不受限。

然而,它也不无缺憾:

  • 性能略逊于 JDK 动态代理,代理对象通过字节码生成产生。
  • 使用稍显复杂,需要创建 MethodInterceptor 的实现类。

JDK与CGLIB动态代理的对比

让我们将 JDK 和 CGLIB 动态代理置于显微镜下,一探究竟:

特性 JDK动态代理 CGLIB动态代理
代理类型 接口 接口或类
代理类继承 InvocationHandler 任意类
性能 优异 稍差
使用难度 简单 稍复杂

代码示例

JDK动态代理

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

interface TargetInterface {
    void doSomething();
}

class TargetClass implements TargetInterface {
    @Override
    public void doSomething() {
        System.out.println("TargetClass.doSomething() called");
    }
}

class LoggingInvocationHandler implements InvocationHandler {
    private final TargetInterface target;

    public LoggingInvocationHandler(TargetInterface target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("LoggingInvocationHandler.invoke() called");
        Object result = method.invoke(target, args);
        System.out.println("LoggingInvocationHandler.invoke() completed");
        return result;
    }
}

public class DynamicProxyExample {
    public static void main(String[] args) {
        TargetInterface target = new TargetClass();
        LoggingInvocationHandler invocationHandler = new LoggingInvocationHandler(target);

        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
            TargetClass.class.getClassLoader(),
            new Class[]{TargetInterface.class},
            invocationHandler
        );

        proxy.doSomething();
    }
}

CGLIB动态代理

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class TargetClass {
    public void doSomething() {
        System.out.println("TargetClass.doSomething() called");
    }
}

class LoggingMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("LoggingMethodInterceptor.intercept() called");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("LoggingMethodInterceptor.intercept() completed");
        return result;
    }
}

public class DynamicProxyExample {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TargetClass.class);
        enhancer.setCallback(new LoggingMethodInterceptor());

        TargetClass proxy = (TargetClass) enhancer.create();
        proxy.doSomething();
    }
}

结论

动态代理技术为软件开发开辟了一条崭新的道路,让开发者在不修改源代码的前提下,为类或接口注入新的活力。JDK动态代理和CGLIB动态代理各具特色,根据不同的需求,选择合适的代理技术,可以有效提升开发效率,增强代码的灵活性和可维护性。

常见问题解答

  1. 动态代理有什么实际应用场景?

    • 日志记录
    • 性能监控
    • 事务管理
    • 权限控制
  2. JDK动态代理和CGLIB动态代理有什么区别?

    • JDK动态代理只能代理接口,而CGLIB动态代理既能代理接口,也能代理类。
  3. 为什么需要使用动态代理?

    • 在不修改源代码的情况下,为代码添加额外功能。
  4. 动态代理的缺点是什么?

    • JDK动态代理无法代理类,CGLIB动态代理性能略差于JDK动态代理。
  5. 如何选择合适的动态代理实现?

    • 如果需要代理接口,并且追求高性能,则选择JDK动态代理;如果需要代理类,或者需要代理类继承自特定的类,则选择CGLIB动态代理。