跳转至

描述动态代理的几种实现方式,分别说明优缺点

一、动态代理是什么

在运行时生成代理类,不写死代理代码。AOP、RPC 都基于它。

二、JDK 动态代理

原理

基于接口,用 java.lang.reflect.Proxy 生成实现类。

interface UserService {
    void save();
}

class UserServiceImpl implements UserService {
    public void save() { System.out.println("save"); }
}

UserService proxy = (UserService) Proxy.newProxyInstance(
    classLoader,
    new Class[]{UserService.class},
    (p, method, args) -> {
        System.out.println("before");
        Object r = method.invoke(new UserServiceImpl(), args);
        System.out.println("after");
        return r;
    }
);
proxy.save();

优缺点

  • ✅ 不需要第三方库,JDK 自带。
  • ✅ 接口改了不用重新生成代理。
  • 只能代理接口,不能代理类。

三、CGLIB 动态代理

原理

基于继承,用 ASM 生成目标类的子类,重写方法。

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    System.out.println("before");
    Object r = proxy.invokeSuper(obj, args);
    System.out.println("after");
    return r;
});
UserService proxy = (UserService) enhancer.create();

优缺点

  • ✅ 可以代理类,不需要接口。
  • ✅ 性能比 JDK 动态代理高(FastClass 机制)。
  • ❌ 不能代理 final 类/方法(不能继承)。
  • ❌ 第三方依赖。

四、对比

JDK 动态代理 CGLIB
基础 接口 继承子类
要求 目标类实现接口 目标类非 final
性能 创建快,调用慢 创建慢,调用快
Spring 默认 有接口用 JDK Spring Boot 2.x 默认 CGLIB

五、其他方式

Javassist

JBoss 用,API 比 ASM 友好,性能中等。

ASM / ByteBuddy

底层字节码操作,性能最高,学习曲线陡。

高频追问

  • 为什么 CGLIB 不能代理 final?因为它生成子类。
  • Spring AOP 怎么选?有接口用 JDK 动态代理,没有接口用 CGLIB。