JAVA反射-2 反射和动态代理结合

123 阅读3分钟

一、核心概念区分

概念角色关键点
反射(Reflection)Java 的基础机制,允许程序在运行时检查和操作类、方法、字段等元数据。MethodFieldConstructor 等类 - 通过 Class.forName()method.invoke() 等 API 实现动态调用
动态代理(Dynamic Proxy)基于反射实现的高级应用,允许在运行时创建代理类,拦截方法调用。Proxy.newProxyInstance()InvocationHandler 接口 - 无需手动编写代理类,由 JVM 动态生成

二、反射在动态代理中的作用

  1. 动态创建代理类
    Proxy.newProxyInstance() 利用反射在运行时生成实现指定接口的代理类字节码,并加载到 JVM 中。
  2. 方法调用的动态转发
    代理对象的方法调用会被转发到 InvocationHandler.invoke(),其中的 Method 对象(反射机制的核心)携带了被调用方法的完整信息,用于精确调用目标方法。

举一个小案例

/**
 * 目标接口:定义服务方法
 * 动态代理会生成实现该接口的代理类
 */
interface MyService {
    String sayHello(String name);
}

/**
 * 接口实现类:真实服务的具体实现
 * 代理对象最终会将方法调用转发到该类的实例
 */
class MyServiceImpl implements MyService {
    @Override
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

/**
 * 自定义InvocationHandler:动态代理的具体实现类
 * 实现invoke方法处理所有代理对象的方法调用
 */
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 {
        /*
         * --------------------- method对象的核心说明 ---------------------
         * method参数是java.lang.reflect.Method类的实例,代表被调用的目标方法
         * 由Java动态代理机制在运行时自动传入,无需手动创建
         * 包含方法名、参数类型、返回类型等完整元数据
         * 
         * 例如:当调用myService.sayHello("World")时,
         * method会封装sayHello方法的所有信息,包括:
         * - 方法名:sayHello
         * - 参数类型:String
         * - 返回类型:String
         */

        // 打印方法调用前的日志,通过method.getName()获取被调用的方法名
        System.out.println("Before method call: " + method.getName()); // Before method call: sayHello
        
        /*
         * 通过method.invoke()反射调用目标对象的方法:
         * 1. target:被代理的目标对象(MyServiceImpl实例)
         * 2. args:方法调用的参数数组(["World"])
         * 3. method对象携带了方法调用的所有必要信息,无需手动指定方法名
         */
        Object result = method.invoke(target, args);
        
        // 打印方法调用后的日志
        System.out.println("After method call: " + method.getName());
        
        return result;
    }
}

public class DynamicProxyDemo {
    public static void main(String[] args) {
        // 创建真实服务对象
        MyService realService = new MyServiceImpl();
        
        // 创建InvocationHandler实例,传入真实服务对象
        MyInvocationHandler handler = new MyInvocationHandler(realService);
        
        /*
         * 创建动态代理对象:
         * 1. ClassLoader:被代理对象的类加载器
         * 2. interfaces:被代理对象需要实现的接口数组(MyService接口)
         * 3. handler:代理对象的处理器
         * 
         * 代理对象会捕获被代理的类&其指定接口的所有方法调用,并转发到handler.invoke()
         * 关键:代理的是接口,而不是类
         */
       // 说白了,就是把MyServiceImpl类的MyService接口由Proxy.newProxyInstance生成代理对象
       // 并将该接口的所有方法交给MyInvocationHandler处理
        MyService myService = (MyService) Proxy.newProxyInstance(
            realService.getClass().getClassLoader(),
            realService.getClass().getInterfaces(),
            handler
        );
        
        /*
         * 调用代理对象的方法:
         * 1. 实际会触发handler.invoke()方法,在方法内部,调用method.invoke,还是调的MyServiceImpl的指定方法
         * 2. method参数会自动封装sayHello方法的反射信息
         * 3. args参数会自动封装方法调用的参数(["World"])
         */
        // 利用{动态代理对象.要调用的方法(参数)}可以实现动态代理执行对应的方法
        String result = myService.sayHello("World"); 
        System.out.println(result); // 输出:Hello, World
    }
}

三、应该场景

技术核心依赖示例
AOP(面向切面编程)动态代理 + 反射Spring 的 @Transactional、日志拦截器
RPC(远程过程调用)动态代理 + 网络通信Dubbo、gRPC 通过代理对象将本地方法调用转换为网络请求
ORM(对象关系映射)动态代理 + 反射MyBatis 的 Mapper 接口代理实现
Mock 测试动态代理Mockito 通过代理对象模拟方法行为