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 动态代理会自动把这个调用转发给 Proxyerinvoke 方法
  3. invoke 里,先执行 "前置增强操作"
  4. 再通过 method.invoke(target, args) 调用目标对象(MyInterfaceImpl)的真实 doSomething() 方法
  5. 最后执行 "后置增强操作"
  6. 返回结果给调用者

核心注意点

  1. 必须基于接口:JDK 动态代理只能代理 "实现了接口的类",不能直接代理没有接口的类
  2. 增强逻辑集中:所有接口方法的增强逻辑都写在 invoke 里,一次编写,所有方法复用
  3. 动态生成:代理对象是运行时创建的,编译期看不到这个类的 .class 文件
  4. invoke 方法会拦截代理对象的 所有接口方法 (包括 Object 类的方法如 toString(),需注意过滤)。
  5. 目标对象的方法若为 final/static,则无法被代理(因为无法通过反射重写)。
相关推荐
kk哥88992 小时前
scala 介绍
开发语言·后端·scala
武子康2 小时前
大数据-181 Elasticsearch 段合并与磁盘目录拆解:Merge Policy、Force Merge、Shard 文件结构一文搞清
大数据·后端·elasticsearch
松莫莫2 小时前
【Spring Boot 实战】使用 Server-Sent Events (SSE) 实现实时消息推送
java·spring boot·后端
小坏讲微服务2 小时前
Spring Boot 4.0 整合 Kafka 企业级应用指南
java·spring boot·后端·kafka·linq
西门老铁2 小时前
解读 Casbin 之二:如何在 Spring项目中实现菜单权限
后端
CodeSheep2 小时前
这个知名编程软件,正式宣布停运了!
前端·后端·程序员
ZePingPingZe2 小时前
Spring Boot常见注解
java·spring boot·后端
SimonKing2 小时前
镜像拉不下来怎么办?境内Docker镜像状态在线监控来了
java·后端·程序员
a程序小傲2 小时前
华为Java面试被问:SQL执行顺序
java·后端·sql·华为·面试