JDK 动态代理: 它的工作原理是什么?它有什么限制?

Python3.8

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

核心定义

JDK 动态代理是 Java 官方提供的一种在运行时动态创建代理对象的技术。它允许我们创建一个“替身”对象,这个替身可以在不修改原始业务代码的情况下,拦截对原始对象方法的调用,并在调用前后插入自定义的逻辑(即 AOP 中的“通知”)。


工作原理

JDK 动态代理的核心是两个组件:java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。

它的工作流程可以分解为以下几个步骤:

1. 前提:定义接口和目标类
首先,你必须有一个接口,以及一个实现了该接口的目标类(也就是你想要代理的原始业务类)。

// 1. 定义接口
public interface SmsService {
    String send(String message);
}

// 2. 目标类,实现接口
public class SmsServiceImpl implements SmsService {
    @Override
    public String send(String message) {
        System.out.println("【核心业务】: 正在发送短信: " + message);
        return "短信发送成功";
    }
}

2. 编写处理器 (InvocationHandler)
你需要创建一个实现了 InvocationHandler 接口的类。这个类是代理的灵魂,所有对代理对象方法的调用最终都会被转发到它的 invoke 方法里。你所有的增强逻辑(比如日志、计时、权限检查)都写在这里。

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

// 3. 编写处理器,这是所有增强逻辑的所在地
public class MyInvocationHandler implements InvocationHandler {
    
    // 持有对目标对象的引用
    private final Object target;

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

    /**
     * 当你调用代理对象的任何方法时,这个 invoke 方法就会被执行。
     * @param proxy   动态生成的代理对象本身 (很少使用)
     * @param method  被调用的目标方法 (例如 send 方法)
     * @param args    调用目标方法时传入的参数
     * @return        目标方法的返回值
     */
    @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("【后置通知】: 方法 " + method.getName() + " 执行完毕。");

        return result; // 返回原始方法的执行结果
    }
}

3. 生成并使用代理对象
最后,使用 Proxy.newProxyInstance() 这个静态方法来动态地创建代理对象。

public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
            // 1. 目标对象的类加载器 (ClassLoader)
            //    用于加载动态生成的代理类。
            target.getClass().getClassLoader(),

            // 2. 目标对象实现的所有接口 (Class<?>[])
            //    代理对象会实现这些接口,告诉代理它需要有哪些方法。
            target.getClass().getInterfaces(),

            // 3. 我们编写的处理器 (InvocationHandler)
            //    将方法调用分派到这里。
            new MyInvocationHandler(target)
        );
    }
}

// --- 客户端使用 ---
public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        SmsService target = new SmsServiceImpl();
        
        // 获取代理对象
        SmsService proxy = (SmsService) JdkProxyFactory.getProxy(target);
        
        // 通过代理对象调用方法
        String response = proxy.send("Hello, JDK Proxy!");
        System.out.println("客户端收到响应: " + response);

        // 代理对象的真实类型是什么?
        System.out.println("代理对象的类型: " + proxy.getClass().getName());
    }
}

运行结果:

【前置通知】: 方法 send 即将执行...
【核心业务】: 正在发送短信: Hello, JDK Proxy!
【后置通知】: 方法 send 执行完毕。
客户端收到响应: 短信发送成功
代理对象的类型: com.sun.proxy.$Proxy0

从结果可以看出,我们调用 proxy.send() 时,MyInvocationHandler 中的前后置逻辑被成功执行了。并且代理对象的类型是一个由 JVM 在运行时动态生成的、名字类似 $Proxy0 的类。


限制与缺点

JDK 动态代理虽然强大,但它有一个非常重要的核心限制:

  1. 必须基于接口进行代理 (Most Important Limitation)

    • 它只能代理实现了接口的类。 这是因为 Proxy.newProxyInstance() 的第二个参数要求传入一个接口数组。生成的代理类会实现这些接口,但它不会继承你的目标类。
    • 因此,如果一个类没有实现任何接口(一个普通的 POJO),JDK 动态代理就无法为它创建代理。这是它最大的局限性,也是为什么 Spring AOP 需要引入 CGLIB 作为补充的原因。
  2. 性能开销

    • 由于其底层是基于 Java 反射 (Reflection) 机制(即 method.invoke(...)),相比直接调用,会存在一定的性能开销。不过,在现代的 JVM 中,反射的性能已经得到了极大的优化,对于绝大多数业务场景来说,这点开销可以忽略不计。
  3. 类型转换问题

    • 代理对象 proxy 的类型是 com.sun.proxy.$Proxy0,它只实现了 SmsService 接口。你不能将它强制类型转换为 SmsServiceImpl
    • proxy instanceof SmsService 会返回 true
    • proxy instanceof SmsServiceImpl 会返回 false
    • 这在某些需要具体实现类类型的场景下可能会导致 ClassCastException

总结

特性描述
核心技术Java 反射 (java.lang.reflect.Proxy, InvocationHandler)
代理方式在运行时动态地实现目标对象的所有接口,生成代理类。
最大限制目标对象必须实现接口。无法代理没有接口的普通类。
优点Java 原生支持,无需引入任何第三方库。
缺点必须有接口;存在一定的反射性能开销;无法强转为具体实现类。

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值