JDK 动态代理与 CGLIB 有何区别?Spring 如何选择?

结论先行:JDK 动态代理基于接口:运行时用 Proxy 生成实现同接口的代理类,方法调用统一转发给 InvocationHandler;CGLIB 基于继承:运行时生成目标类的子类并重写非 final 方法。Spring AOP 默认策略是有接口用 JDK 代理、无接口用 CGLIB,而 Spring Boot 2.x 起默认全程使用 CGLIB。

两者对比

维度JDK 动态代理CGLIB
原理接口 + InvocationHandler 转发生成子类 + 字节码增强(ASM)
前提目标必须有接口类不可 final,方法不可 final/static
创建方式Proxy.newProxyInstanceEnhancer.create
额外依赖JDK 自带依赖 cglib(Spring 内置)
性能代理类创建快,调用走反射转发创建慢,调用走增强字节码、更快
import java.lang.reflect.*;

public class ProxyDemo {
    interface Say { void hi(); }

    static class Real implements Say {
        public void hi() { System.out.println("real hi"); }
    }

    public static void main(String[] args) {
        Real target = new Real();
        Say proxy = (Say) Proxy.newProxyInstance(
                Real.class.getClassLoader(),
                new Class<?>[]{Say.class},
                (p, method, a) -> {
                    System.out.println("before");
                    Object r = method.invoke(target, a);
                    System.out.println("after");
                    return r;
                });
        proxy.hi();
    }
}

常见追问 / 记忆点

  • 记忆点:JDK 代理要接口、CGLIB 要可继承;Spring Boot 2+ 默认 CGLIB(proxyTargetClass=true)。
  • 追问:类内部 this 调本类方法不会经过代理对象,因此自调用场景切面会失效。
  • 追问:Spring 早期优先 JDK 代理,是因为接口是解耦规范,且 JDK 代理类生成开销更小。
笔记加载中…