浅谈Spring中JDK动态代理和CGLIB动态代理

本文介绍了代理模式,特别是动态代理的概念及其应用。详细讲解了JDK动态代理,通过拦截器和反射机制实现接口的代理。接着讨论了CGLIB动态代理,它是基于字节码生成库,通过继承目标对象来实现。文中还对比了JDK与CGLIB的性能,并指出在Spring中如何选择使用这两种代理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是代理模式

​ 代理模式(Proxy Pattern)给某一个对象提供一个代理,并且由代理对象控制原对象得引用,代理对象在客户端和目标对象之间起到中间作用。

​ 代理模式是常用得结构设计模式之一,当直接访问某些对象存在问题时候可以通过一个代理对象间接访问,为了保证客户端使用透明性,所访问得真实对象需要实现相同得接口。

代理模式可以分为静态代理和动态代理两种类型,而动态代理中又分为JDK动态代理和CGLIB代理两种。我们今天主要讲动态代理。

动态代理定义及应用

​ 动态代理:是使用反射和字节码的技术,在运行期创建指定接口或类的子类,以及其实例对象的技术,通过这个技术可以无侵入的为代码进行增强。

​ 动态代理应用非 常的广泛,在各种开源的框架中都能看到他们的身影,比如spring中的aop使用动态代理增强,mybatis中使用动态代理生成mapper。

JDK实现的动态代理

解析

利用拦截器(必须实现InvocationHandler接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

jdk实现的动态代理由两个重要的成员组成,分别是ProxyInvocationHandler

  • Proxy: 是所有动态代理的父类,它提供了一个静态方法来创建动态代理的class对象和实例

  • InvocationHandler: 每个动态代理实例都有一个关联的InvocationHandler,在代理实例上调用方法是,方法调用将被转发到InvocationHandler的invoke方法

1、代码实现

IStudentService接口


/**
 * @Author charles.yao
 * @Description 抽象角色,一般都是用接口,或者抽象类解决。
 * @Date 2022/10/21 15:32
 */
public interface IStudentService {
    /**
     * 增加
     */
    public void addStudent();

    /**
     * 删除
     */
    public void deleteStudent();

    /**
     * 更新
     */
    public void updateStudent();

    /**
     * 查询
     */
    public void selectStudent();
}

实现类StudentServiceImpl


/**
 * @Author charles.yao
 * @Description 学生信息service
 * @Date 2022/10/21 15:32
 */
@Slf4j
@Service
public class StudentServiceImpl implements IStudentService {
    @Override
    public void addStudent() {
        log.info("执行了addStudent");
    }
### Spring AOP 中 JDK 动态代理CGLIB 的区别 #### 区别概述 JDK 动态代理主要依赖于 Java 内置的 `java.lang.reflect.Proxy` 类来创建代理对象,而这些代理对象必须实现一个或多个接口。这种方式适用于目标类实现了至少一个接口的情况[^1]。 相比之下,CGLIB 是一种高性能的字节码生成库,它可以在运行时扩展 Java 类并定义新的方法。对于那些没有提供任何接口的目标类来说,CGLIB 提供了一种有效的解决方案。通过继承原始类并重写其非最终的方法,CGLIB 能够实现代理功能[^2]。 #### 实现原理 - **JDK 动态代理** 当使用 JDK 动态代理时,实际是在内存中构建了一个实现了相同接口的新类实例,并在这个新实例上调用指定的方法前/后执行额外逻辑。此过程利用了反射机制,因此性能上可能会稍逊一些,尤其是在频繁调用的情况下[^3]。 ```java // 创建 InvocationHandler 来处理方法调用 InvocationHandler handler = new MyInvocationHandler(target); // 获取被代理对象所实现的所有接口 Class<?>[] interfaces = target.getClass().getInterfaces(); // 返回由给定加载器接口数组构造的一个代理类的新实例 return Proxy.newProxyInstance(loader, interfaces, handler); ``` - **CGLIB** 对于未实现接口的对象,则采用 CGLIB 库来进行代理操作。CGLIB 可以直接子类化现有类,并覆盖其中的方法以便插入自定义行为。由于不需要借助接口,所以适用范围更广;不过需要注意的是,如果原类中有 final 方法则无法被覆写,也就意味着这部分不会受到 AOP 切面的影响[^4]。 ```java Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Target.class); // 设置要代理的父类 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 执行前置通知或其他业务逻辑... Object result = proxy.invokeSuper(obj, args); // 调用父类方法 // 执行后置通知或其他业务逻辑... return result; } }); Target proxy = (Target) enhancer.create(); // 创建代理对象 ``` #### 性能对比 通常情况下,CGLIB 在某些特定场景下可能比 JDK 动态代理表现得更好,特别是在大量实例化的环境中。然而,在大多数应用场景里两者之间的差异并不明显,除非有非常严格的性能需求才需考虑这一点。另外值得注意的是,随着 JVM 编译优化的进步,这种差距正在逐渐缩小。 #### 使用场景建议 - 如果应用程序中的服务层组件都遵循良好的面向接口编程原则(即每个具体的服务都有对应的接口),那么推荐优先选用 JDK 动态代理方式。 - 若遇到确实存在不带接口的具体类需要加入到 AOP 处理链路之中,此时应该选择 CGLIB 方案作为替代方案之一。 - 还有一点很重要,当涉及到对私有成员变量的操作或是希望避免因修改原有代码结构带来的风险时,也可以倾向于使用 CGLIB 技术。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值