Java中的动态代理

114 阅读3分钟

简单总结

如果我要增强目标对象的一些方法,就要将目标对象需要增强的这些方法抽取为一个接口并让目标对象实现这个接口,代理对象中必须有目标对象作为成员变量,且需要实现InvocationHandler接口并重写invoke方法,最后使用时调用Proxy.newProxyInstance传入目标对象的类加载器和实现的接口和实现了InvocationHandler的代理对象最后强转成抽取的需要增强的接口的实现类,这时就可以调用接口中定义的方法了

JDK动态代理具体使用步骤详解

1. 抽取目标方法到接口

// 定义接口(抽取需要增强的方法)
public interface DataService {
    void saveData(String data);
    String fetchData();
}

2. 目标类实现接口

// 真实对象(实现接口)
public class DataServiceImpl implements DataService {
    @Override
    public void saveData(String data) {
        System.out.println("保存数据: " + data);
    }

    @Override
    public String fetchData() {
        return "真实数据";
    }
}

3. 代理类实现InvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DataServiceProxy implements InvocationHandler {
    // 持有目标对象(必须)
    private final Object target;

    public DataServiceProxy(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("[代理] 方法调用后");
        return result;
    }
}

4. 生成代理对象

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        // 创建真实对象
        DataServiceImpl realService = new DataServiceImpl();

        // 创建InvocationHandler(绑定真实对象)
        DataServiceProxy handler = new DataServiceProxy(realService);

        // 动态生成代理对象
        DataService proxy = (DataService) Proxy.newProxyInstance(
            realService.getClass().getClassLoader(),  // 类加载器
            realService.getClass().getInterfaces(),   // 目标实现的接口
            handler                                   // 代理逻辑处理器
        );

        // 通过代理调用方法
        proxy.saveData("测试数据");
        System.out.println("获取数据: " + proxy.fetchData());
    }
}

输出结果

[代理] 方法调用前: saveData
保存数据: 测试数据
[代理] 方法调用后
[代理] 方法调用前: fetchData
[代理] 方法调用后
获取数据: 真实数据

关键细节补充

  1. 为什么必须用接口?
    JDK动态代理基于接口实现,代理类会实现指定接口,因此无法代理没有接口的类(此时需用CGLIB)。

  2. InvocationHandler的作用
    它是代理逻辑的核心容器,所有通过代理对象调用的方法都会路由到它的invoke方法。

  3. Proxy.newProxyInstance参数详解

    参数作用示例值
    类加载器定义代理类的类加载器target.getClass().getClassLoader()
    接口数组代理类要实现的接口target.getClass().getInterfaces()
    InvocationHandler代理逻辑的实现new MyHandler(target)

常见问题解答

  1. 如何选择性增强某些方法?
    invoke方法中通过method.getName()判断:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("saveData".equals(method.getName())) {
            System.out.println("增强保存操作");
        }
        return method.invoke(target, args);
    }
    
  2. 能代理多个接口吗?
    可以!只需在接口数组中传入多个接口:

    new Class[]{Interface1.class, Interface2.class}
    
  3. 代理对象和真实对象的关系
    代理对象是JVM动态生成的类(类名通常以$Proxy开头),与真实对象是兄弟关系(都实现相同接口),而非子类。