Skip to content

动态代理

动态代理是在不修改目标对象源代码的情况下,在程序运行时动态地生成一个代理对象。这个代理对象拥有和目标对象一样的接口,但它在调用目标对象方法之前或之后,可以插入我们自己的附加逻辑。

两种主要实现方式

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

这是 Java 标准库自带的动态代理实现方式。

核心原理:JDK 动态代理只能为实现了接口的类创建代理。它会生成一个实现了目标对象所有接口的代理类,这个代理类和目标对象实现了相同的接口,因此可以被视为目标对象的替身

关键组成:

  • java.lang.reflect.Proxy类:创建代理对象的工具类
  • java.lang.reflect.InvoccationHandler接口:核心,需要在它的invoke()方法中编写代理逻辑。每当代理对象的方法被调用时,实际执行的就是InvocationHandlerinvoke()方法。

工作流程:

  1. 定义一个接口(比如UserService和他的实现类UserServiceImpl)。
  2. 创建一个InvacationHandler的实现类,把UserServiceImpl对象作为参数传给它。在invoke()方法里,可以在调用UserServiceImpl的实际方法前后添加额外的逻辑。
  3. 使用Proxy.newProxyInstance()方法,传入类加载器、目标对象实现的接口数组、以及你的InvocationHandler实例,来生成代理对象。
  4. 通过代理对象调用方法时,这个调用会被转发到InvocationHandlerinvoke()方法,在那里额外逻辑会执行,然后invoke()再通过反射调用目标对象的实际方法。

代码示例:

    // 1. 定义接口
     public interface UserService {
         void addUser(String name);
         String getUser(int id);
     }

     // 2. 实现类 (目标对象)
     public class UserServiceImpl implements UserService {
         @Override
         public void addUser(String name) {
             System.out.println("用户服务:正在添加用户:" + name);
             // 模拟数据库操作
         }

         @Override
         public String getUser(int id) {
             System.out.println("用户服务:正在获取用户 ID:" + id);
             return "用户" + id;
         }
     }

     // 3. 动态代理处理器 (InvocationHandler)
     import java.lang.reflect.InvocationHandler;
     import java.lang.reflect.Method;
     import java.lang.reflect.Proxy;

     public class MyInvocationHandler implements InvocationHandler {
         private Object target; // 目标对象

         public MyInvocationHandler(Object target) {
             this.target = target;
         }

         @Override
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             // 在调用目标方法之前执行的逻辑(前置增强)
             System.out.println("--- JDK代理:方法 " + method.getName() + " 开始执行 ---");

             // 调用目标对象的方法
             Object result = method.invoke(target, args);

             // 在调用目标方法之后执行的逻辑(后置增强)
             System.out.println("--- JDK代理:方法 " + method.getName() + " 执行结束 ---");

             return result; // 返回目标方法的执行结果
         }

         // 获取代理对象的方法
         public Object getProxy() {
             // 创建代理对象:
             // 参数1:类加载器,通常和目标对象使用同一个类加载器
             // 参数2:目标对象实现的接口数组,代理对象会实现这些接口
             // 参数3:InvocationHandler 实例,负责处理代理对象的每一次方法调用
             return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                         target.getClass().getInterfaces(),
                                         this);
         }
     }

     // 4. 测试
     public class JdkProxyDemo {
         public static void main(String[] args) {
             // 创建目标对象
             UserService userService = new UserServiceImpl();

             // 创建InvocationHandler
             MyInvocationHandler handler = new MyInvocationHandler(userService);

             // 获取代理对象
             UserService proxy = (UserService) handler.getProxy();

             // 通过代理对象调用方法
             proxy.addUser("张三");
             System.out.println("--------------------");
             proxy.getUser(101);
         }
     }

2. CGLIB 动态代理(基于类)

JDK 动态代理的限制是它只能代理接口。如果一个类没有实现任何接口,那么 JDK 动态代理就无能为力了。

核心原理:

CGLIB 是一个强大的高性能字节码生成库。它能够在运行时子类化(继承)一个类,然后重写父类的方法来达到代理的目的。因此,CGLIB 代理不需要目标对象实现接口,它可以直接代理普通的类。

关键组成:

  • net.sf.cglib.proxy.Enhancer 类: 这是创建代理对象的核心类。
  • net.sf.cglib.proxy.MethodInterceptor 接口: 类似于 JDK 代理的 InvocationHandler,你需要在它的 intercept() 方法中编写代理逻辑。

工作流程:

  1. 你有一个普通的类 (比如 ProductService),可能没有实现任何接口。
  2. 你创建一个 MethodInterceptor 的实现类,并在 intercept() 方法中定义代理逻辑,包括调用目标对象的方法。
  3. 使用 Enhancer 类来设置父类 (目标类) 和回调函数 (MethodInterceptor)。
  4. 调用 enhancer.create() 生成代理对象。这个代理对象是目标类的子类。
  5. 当你通过 CGLIB 生成的代理对象调用方法时,这个调用会被转发到 MethodInterceptor 的 intercept() 方法,在那里你的附加逻辑会执行,然后 intercept() 再通过 MethodProxy 调用父类(目标类)的对应方法。

示例代码:

    // 1. 普通类 (目标对象),无需实现接口
    public class ProductService {
        public void addProduct(String name) {
            System.out.println("产品服务:正在添加产品:" + name);
        }

        public String getProduct(int id) {
            System.out.println("产品服务:正在获取产品 ID:" + id);
            return "产品" + id;
        }
    }

    // 2. 动态代理处理器 (MethodInterceptor)
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;

    public class MyMethodInterceptor implements MethodInterceptor {
        private Object target; // 目标对象

        public MyMethodInterceptor(Object target) {
            this.target = target;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            // 在调用目标方法之前执行的逻辑(前置增强)
            System.out.println("--- CGLIB代理:方法 " + method.getName() + " 开始执行 ---");

            // 调用目标对象的方法
            // proxy.invokeSuper(obj, args) 是调用父类(即目标对象)的方法
            Object result = proxy.invokeSuper(obj, args);

            // 在调用目标方法之后执行的逻辑(后置增强)
            System.out.println("--- CGLIB代理:方法 " + method.getName() + " 执行结束 ---");

            return result; // 返回目标方法的执行结果
        }

        // 获取代理对象的方法
        public Object getProxy() {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass()); // 设置父类为目标对象类
            enhancer.setCallback(this); // 设置回调为当前的 MethodInterceptor
            return enhancer.create(); // 创建代理对象(目标类的子类)
        }
    }

    // 3. 测试
    public class CglibProxyDemo {
        public static void main(String[] args) {
            // 创建目标对象
            ProductService productService = new ProductService();

            // 创建MethodInterceptor
            MyMethodInterceptor interceptor = new MyMethodInterceptor(productService);

            // 获取代理对象
            ProductService proxy = (ProductService) interceptor.getProxy();

            // 通过代理对象调用方法
            proxy.addProduct("电脑");
            System.out.println("--------------------");
            proxy.getProduct(202);
        }
    }

动态代理的优点与应用

优点

  • 代码解耦: 将横切关注点(如日志、事务、安全)从核心业务逻辑中分离出来,提高了代码的内聚性。
  • AOP 的基石: 动态代理是实现 AOP 的核心技术之一,Spring 框架的事务管理、日志记录等都是基于动态代理实现的。
  • 不修改源代码: 可以在不改变原有类结构的基础上,增强其功能。
  • 运行时增强: 功能的增强是在程序运行时动态完成的,非常灵活。

常见应用场景

  • Spring AOP: Spring 的声明式事务
  • @Transactional 就是通过动态代理实现的。
  • RPC 框架: 远程方法调用,代理对象负责网络通信和序列化/反序列化。
  • ORM 框架: 如 Hibernate,延迟加载(Lazy Loading)功能可能就用到了动态代理。
  • 缓存: 在方法调用前后添加缓存逻辑。
  • 权限校验: 在方法执行前检查用户权限。
  • 日志记录: 统一记录方法的入参、出参和执行时间。