欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Spring实现AOP功能的原理:代理模式(JDK动态代理与CGLIB字节码生成技术代理)的理解

Spring实现AOP功能的原理:代理模式(JDK动态代理与CGLIB字节码生成技术代理)的理解

2025/2/10 3:13:11 来源:https://blog.csdn.net/liuruiaaa/article/details/145510858  浏览:    关键词:Spring实现AOP功能的原理:代理模式(JDK动态代理与CGLIB字节码生成技术代理)的理解

JDK 动态代理和 CGLIB 代理是 Java 中两种常见的动态代理技术。它们的核心作用是 在运行时生成代理对象,用于增强原始对象的功能(如 AOP 切面编程、拦截方法调用等)。


JDK 动态代理

JDK 动态代理基于 java.lang.reflect.ProxyInvocationHandler 接口。它 仅能代理实现了接口的类,通过 Proxy.newProxyInstance 方法在运行时生成代理对象。

工作原理

  1. 代理类实现目标接口,并在 invoke 方法中拦截所有方法调用。
  2. 代理对象是 Proxy 类的子类,JVM 在运行时动态生成该类。
  3. 调用方法时,代理对象会调用 InvocationHandlerinvoke 方法。

示例

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 1. 定义接口
interface UserService {void save(String name);
}// 2. 目标对象
class UserServiceImpl implements UserService {@Overridepublic void save(String name) {System.out.println("保存用户: " + name);}
}// 3. 创建 InvocationHandler 处理器
class MyInvocationHandler implements InvocationHandler {private final Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("日志: 方法 " + method.getName() + " 被调用,参数:" + args[0]);Object result = method.invoke(target, args);System.out.println("日志: 方法 " + method.getName() + " 调用完成");return result;}
}// 4. 使用动态代理创建代理对象
public class JDKProxyDemo {public static void main(String[] args) {UserService target = new UserServiceImpl();UserService proxy = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new MyInvocationHandler(target));proxy.save("张三");}
}

输出

日志: 方法 save 被调用,参数:张三
保存用户: 张三
日志: 方法 save 调用完成

优缺点

优点

  • 依赖 JDK,直接使用,无需额外库。
  • 线程安全,稳定性较高。

缺点

  • 只能代理实现了接口的类,不能代理普通类。
  • 由于反射调用 invoke性能稍差(比 CGLIB 慢)。

CGLIB 动态代理

CGLIB(Code Generation Library)是一个强大的 字节码生成技术,可以代理 没有实现接口的普通类,其底层依赖 ASM(Java 字节码操作库)。

工作原理

  1. CGLIB 生成 目标类的子类,重写其方法,并在方法调用时增强功能。
  2. 由于 CGLIB 直接操作字节码,比 JDK 代理更快(因为少了反射调用)。
  3. 但由于是继承方式,不能代理 final 方法

示例

需要 引入 CGLIB 依赖

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;// 1. 目标类(没有实现接口)
class UserService {public void save(String name) {System.out.println("保存用户: " + name);}
}// 2. CGLIB 代理类
class CglibProxy implements MethodInterceptor {private final Object target;public CglibProxy(Object target) {this.target = target;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("日志: 方法 " + method.getName() + " 被调用,参数:" + args[0]);Object result = proxy.invoke(target, args);System.out.println("日志: 方法 " + method.getName() + " 调用完成");return result;}// 生成代理对象public Object createProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return enhancer.create();}
}// 3. 使用 CGLIB 创建代理对象
public class CGLIBProxyDemo {public static void main(String[] args) {UserService target = new UserService();UserService proxy = (UserService) new CglibProxy(target).createProxy();proxy.save("李四");}
}

输出

日志: 方法 save 被调用,参数:李四
保存用户: 李四
日志: 方法 save 调用完成

优缺点

优点

  • 可以代理普通类(不要求接口)。
  • 性能比 JDK 代理更好(不使用反射,而是直接生成字节码)。

缺点

  • 不能代理 final 类或 final 方法(因为 CGLIB 通过继承来生成代理)。
  • 额外依赖 CGLIB,需要额外的 Jar 包(不过 Spring 已内置 CGLIB)。
  • 比 JDK 代理稍微复杂,但更灵活。

JDK 代理 vs CGLIB 代理 对比

特性JDK 动态代理CGLIB 代理
代理方式反射 InvocationHandler继承+ASM 字节码生成
是否需要接口需要(只能代理接口)不需要(可代理普通类)
代理对象类型Proxy 生成的匿名类目标类的子类
是否可代理 final 方法可以不可以
性能较慢(基于反射)较快(直接修改字节码)
依赖JDK 自带需额外依赖 CGLIB
典型应用java.lang.reflect.Proxy、Spring AOP(基于接口)Spring AOP(无接口时)

使用场景总结

  • JDK 动态代理 适用于 已有接口 的情况,比如 Service 层的 UserServiceOrderService
  • CGLIB 动态代理 适用于 无接口 的普通类,比如 UserService 直接写成普通类。

Spring AOP 使用情况

  • 如果被代理的类实现了接口,Spring 默认使用 JDK 动态代理
  • 如果没有接口,Spring 使用 CGLIB 代理@EnableAspectJAutoProxy(proxyTargetClass = true) 强制 CGLIB)。

总结

  • JDK 动态代理 是基于接口的反射机制,适用于 接口代理
  • CGLIB 代理 通过 字节码增强 来生成子类,适用于 普通类代理
  • Spring AOP 结合两者,默认用 JDK,必要时才用 CGLIB。

对于日常开发,如果类有接口,优先使用 JDK 动态代理;否则,使用 CGLIB

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com