JDK 动态代理

Java 动态代理(JDK 动态代理)

适用场景

  • 日志记录、权限校验、事务管理、性能监控等横切逻辑(AOP 思想的核心实现)。
  • 需在不修改目标对象代码的前提下,对方法进行增强(符合开闭原则)。

不要改代码,就用一个简单示例就可以,把这件事情和流程说清楚就可以了

核心思想

JDK 动态代理的核心是:不手动写代理类,运行时动态生成代理对象,在不修改目标对象代码的前提下,对其方法进行增强(如日志、前置后置操作)。

核心依赖两个关键类 / 接口:

  • java.lang.reflect.Proxy:负责动态生成代理实例
  • java.lang.reflect.InvocationHandler:负责定义方法增强的逻辑

核心流程(4 步走)

第一步:定义业务接口(被代理的 "规则")

接口是动态代理的基础,定义了目标对象要实现的方法,代理对象会 "伪装" 成这个接口的实现类。

java 复制代码
// 业务接口:定义要被代理的方法
public interface MyInterface {
    void doSomething();
}

第二步:实现接口(目标对象,真实业务逻辑)

这是真正干活的类,实现接口的方法,包含核心业务逻辑,是我们要 "增强" 的对象。

java 复制代码
// 接口实现类:目标对象,真实业务逻辑的载体
public class MyInterfaceImpl implements MyInterface {
    @Override
    public void doSomething() {
        System.out.println("Doing something..."); // 核心业务
    }
}

第三步:实现 InvocationHandler(增强逻辑处理器)

这是代理的 "核心大脑",所有增强操作(方法执行前 / 后做什么)都写在这里。每次调用代理对象的方法,都会触发 invoke 方法。

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// 增强处理器:实现 InvocationHandler 接口,定义增强逻辑
public class Proxyer implements InvocationHandler {

    private Object target; // 存储目标对象(被代理的真实对象)

    // 构造器:传入目标对象,建立关联
    public Proxyer(Object target) {
        this.target = target;
    }

    /**
     * 代理逻辑触发点:调用代理对象的方法时,自动执行此方法
     * @param proxy 动态生成的代理实例(一般不用)
     * @param method 当前被调用的目标方法(反射对象)
     * @param args 目标方法的参数(无参则为null)
     * @return 目标方法的返回值
     * @throws Throwable 目标方法可能抛出的异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 方法执行前的增强操作
        System.out.println("调用方法前的操作");
        
        // 2. 调用目标对象的真实方法(核心业务逻辑执行)
        Object result = method.invoke(target, args); // 反射调用真实方法
        
        // 3. 方法执行后的增强操作
        System.out.println("调用方法后的操作");
        
        return result; // 返回目标方法的结果(如果有返回值的话)
    }
}

第四步:生成代理对象 + 测试

通过 Proxy.newProxyInstance() 动态生成代理对象,之后调用代理对象的方法,就会自动触发增强逻辑 + 真实业务逻辑。

java 复制代码
import java.lang.reflect.Proxy;

// 测试类:验证代理效果
public class Test {
    public static void main(String[] args) {
        // 1. 创建目标对象(真实干活的对象)
        MyInterface target = new MyInterfaceImpl();
        
        // 2. 创建增强处理器,传入目标对象(建立代理和目标的关联)
        Proxyer handler = new Proxyer(target);
        
        // 3. 动态生成代理对象(核心步骤)
        MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
            MyInterface.class.getClassLoader(), // 类加载器(用接口的类加载器)
            new Class[]{MyInterface.class},     // 代理要实现的接口(和目标对象一致)
            handler                             // 增强逻辑处理器
        );
        
        // 4. 调用代理对象的方法(触发增强逻辑 + 真实业务)
        proxyInstance.doSomething();
    }
}

运行结果

plaintext

erlang 复制代码
调用方法前的操作
Doing something...
调用方法后的操作

关键流程拆解(一句话看懂)

  1. 我们调用 proxyInstance.doSomething()(代理对象的方法)
  2. JDK 动态代理会自动把这个调用转发给 Proxyer 的 invoke 方法
  3. 在 invoke 里,先执行 "前置增强操作"
  4. 再通过 method.invoke(target, args) 调用目标对象(MyInterfaceImpl)的真实 doSomething() 方法
  5. 最后执行 "后置增强操作"
  6. 返回结果给调用者

核心注意点

  1. 必须基于接口:JDK 动态代理只能代理 "实现了接口的类",不能直接代理没有接口的类
  2. 增强逻辑集中:所有接口方法的增强逻辑都写在 invoke 里,一次编写,所有方法复用
  3. 动态生成:代理对象是运行时创建的,编译期看不到这个类的 .class 文件
  4. invoke 方法会拦截代理对象的 所有接口方法 (包括 Object 类的方法如 toString(),需注意过滤)。
  5. 目标对象的方法若为 final/static,则无法被代理(因为无法通过反射重写)。
相关推荐
猿究院_夏燊8 小时前
Spring Bean 生命周期
java·后端·spring
CCblandy9 小时前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
我是人✓9 小时前
SpringBoot项目的五种创建方式
java·spring boot·后端
念念清晰11 小时前
000-OpenJDK17 源码构建:MacOS环境(M芯片)
java·后端
来福猿11 小时前
SpringCloud Config 源码分析:配置拉取、刷新与多环境管理
后端·spring·spring cloud
IT_陈寒13 小时前
React组件莫名重渲染?你可能忘了这个小细节
前端·人工智能·后端
用户98883218717414 小时前
给 .NET 网络调用设定“预算”:用 CancellationTokenSource.CancelAfter 处理超时取消
后端
清风儿32914 小时前
一块不会说话的电表,把我们折腾了半个月
后端
预知同行14 小时前
深入解析 MoE 混合专家模型:从 Router 到 All-to-All 的架构全景与生产实践
后端·架构
upala14 小时前
【仓颉语言入门 · 第二篇 第 8 课】
后端