JDK动态代理和CGLib动态代理

15 22~29 min

JDK动态代理和CGLib动态代理实现

JDK动态代理和CGLib动态代理是Java中实现AOP(面向切面编程)的两种核心技术,它们在实现方式、依赖关系和使用场景上有显著差异。以下是对两者的详细对比:


JDK动态代理

用法

  1. 依赖接口:被代理的类必须实现至少一个接口。

  2. 核心类

    • java.lang.reflect.Proxy:生成代理对象。

    • java.lang.reflect.InvocationHandler:定义代理逻辑。

  3. 实现步骤

    // 1. 定义接口
    public interface UserService {
        void addUser(String name);
    }
    
    // 2. 实现接口
    public class UserServiceImpl implements UserService {
        public void addUser(String name) {
            System.out.println("添加用户: " + name);
        }
    }
    
    // 3. 实现InvocationHandler
    public class LogHandler implements InvocationHandler {
        private final Object target;
    
        public LogHandler(Object target) {
            this.target = target;
        }
    
        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("调用后");
            return result;
        }
    }
    
    // 4. 生成代理对象
    UserService proxy = (UserService) Proxy.newProxyInstance(
        UserService.class.getClassLoader(),
        new Class[]{UserService.class},
        new LogHandler(new UserServiceImpl())
    );
    proxy.addUser("Alice");

特点

  • 基于接口:只能代理接口中定义的方法。

  • 无需依赖:JDK自带,无需第三方库。

  • 反射调用:通过反射执行目标方法,性能略低。


CGLib动态代理

用法

  1. 继承方式:通过生成被代理类的子类覆盖方法。

  2. 核心类

    • Enhancer:生成代理对象。

    • MethodInterceptor:定义代理逻辑。

  3. 实现步骤

    // 1. 目标类(无需接口)
    public class UserService {
        public void addUser(String name) {
            System.out.println("添加用户: " + name);
        }
    }
    
    // 2. 实现MethodInterceptor
    public class LogInterceptor implements MethodInterceptor {
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("调用前: " + method.getName());
            Object result = proxy.invokeSuper(obj, args); // 调用父类方法
            System.out.println("调用后");
            return result;
        }
    }
    
    // 3. 生成代理对象
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(UserService.class);
    enhancer.setCallback(new LogInterceptor());
    UserService proxy = (UserService) enhancer.create();
    proxy.addUser("Bob");

特点

  • 基于继承:可代理未实现接口的类。

  • 依赖第三方库:需引入cglib或Spring的spring-core

  • 字节码操作:通过ASM直接生成字节码,性能更高。

  • 限制:无法代理final类或方法。


核心区别

特性

JDK动态代理

CGLib动态代理

实现方式

基于接口(实现相同接口)

基于继承(生成目标类的子类)

依赖

JDK内置

需第三方库(如cglib

性能

生成快,执行慢(反射调用)

生成慢,执行快(直接调用)

限制

必须实现接口

无法代理final类或方法

应用场景

接口明确的场景(如RPC调用)

无接口或需要高性能的场景(如Spring AOP)


使用场景

  • JDK动态代理:适用于接口明确的场景(如远程方法调用、服务层接口)。

  • CGLib动态代理:适用于无接口的类或需要高性能的场景(如Spring默认使用CGLib代理未实现接口的类)。


Spring中的选择

  • 默认行为:若目标类实现接口,Spring使用JDK代理;否则使用CGLib。

  • 强制CGLib:可通过@EnableAspectJAutoProxy(proxyTargetClass = true)强制使用CGLib。


总结

  • JDK动态代理更轻量但依赖接口,适合简单场景。

  • CGLib动态代理更灵活且性能更高,适合复杂场景但需注意依赖和限制。根据项目需求选择合适方案。

开启新对话