Skip to content
极高进阶

一句话答案

代理模式为目标对象提供替身控制访问,JDK 动态代理基于接口,CGLIB 基于继承生成子类,Spring AOP 的核心。

核心要点

代理模式为目标对象提供一个替身,以控制对目标对象的访问。Java 中有三种实现方式:

1. 静态代理

手动编写代理类,实现与目标类相同的接口:

java
public interface UserService {
    void save(User user);
}

public class UserServiceImpl implements UserService {
    public void save(User user) { /* 保存用户 */ }
}

// 静态代理类
public class UserServiceProxy implements UserService {
    private UserService target;
    
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    
    public void save(User user) {
        System.out.println("开始事务...");
        target.save(user);                      // 委托给目标对象
        System.out.println("提交事务...");
    }
}
  • 缺点:每个接口都要写一个代理类,代码膨胀

2. JDK 动态代理(基于接口)

运行时通过反射生成代理类,目标类必须实现接口

java
public class JdkProxyHandler implements InvocationHandler {
    private Object target;
    
    public JdkProxyHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置增强:" + method.getName());
        Object result = method.invoke(target, args); // 反射调用目标方法
        System.out.println("后置增强:" + method.getName());
        return result;
    }
    
    // 获取代理对象
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),   // 必须有接口
            new JdkProxyHandler(target)
        );
    }
}

// 使用
UserService proxy = JdkProxyHandler.createProxy(new UserServiceImpl());
proxy.save(user); // 调用时会经过 invoke 方法

3. CGLIB 动态代理(基于继承)

通过字节码技术生成目标类的子类作为代理,不要求目标类实现接口:

java
public class CglibProxy implements MethodInterceptor {
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, 
                           MethodProxy proxy) throws Throwable {
        System.out.println("前置增强");
        Object result = proxy.invokeSuper(obj, args); // 调用父类方法
        System.out.println("后置增强");
        return result;
    }
    
    public static <T> T createProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);           // 设置父类
        enhancer.setCallback(new CglibProxy());
        return (T) enhancer.create();
    }
}

三种方式对比:

维度静态代理JDK 动态代理CGLIB
实现方式手写代理类Proxy + InvocationHandler字节码生成子类
要求实现相同接口目标类必须有接口目标类不能是 final
性能编译期确定,最快反射调用,较慢字节码直接调用,略快于 JDK
Spring 选择有接口时默认使用无接口时使用(Spring Boot 2.x+ 默认 CGLIB)

Spring AOP 的代理选择逻辑:

  • 目标类实现了接口 → 默认 JDK 动态代理
  • 目标类没有实现接口 → 使用 CGLIB
  • Spring Boot 2.x 之后默认都使用 CGLIB(spring.aop.proxy-target-class=true

→ 详见 Module 07 Spring AOP 部分

追问与易错

追问方向:

  • JDK 动态代理为什么必须有接口?(Proxy.newProxyInstance 需要接口)
  • CGLIB 为什么不能代理 final 方法?(CGLIB 通过继承生成子类,final 不可重写)
  • Spring AOP 默认用哪种代理?(有接口用 JDK,无接口用 CGLIB;Boot 2.x 默认 CGLIB)

易错点:

  • ❌ "代理模式和装饰器模式一样"——代理控制访问,装饰器增强功能
  • ❌ "JDK 代理比 CGLIB 慢"——JDK8+ 两者性能差距很小

💡 记忆锚点

代理是明星的经纪人:静态代理是一个明星配一个经纪人(太累),JDK动态代理是经纪公司按合同(接口)派人,CGLIB是直接派个替身演员(继承子类)。Spring AOP整套事务/缓存/异步都靠经纪人干活。