JDK动态代理到底生成什么样的类?

本文详细介绍了JDK动态代理的实现机制,包括运行时生成新类、转发接口方法调用到InvocationHandler的invoke方法,以及如何使用`Proxy.newProxyInstance`创建代理实例。通过实例代码演示了如何拦截和增强方法调用的过程。
摘要由CSDN通过智能技术生成

JDK动态代理实现原理以及手写实现JDK动态代理

package cn.ybzy.demo.proxy.proxy;

import cn.ybzy.demo.proxy.client.IUser;

import java.lang.reflect.*;


public class $Proxy0 implements cn.ybzy.demo.proxy.client.IUser {
    MyInvocationHandler invocationHandler;

    public $Proxy0(MyInvocationHandler invocationHandler) {
        this.invocationHandler = invocationHandler;
    }

    public java.lang.Double expenses() {
        try {
            Method m = cn.ybzy.demo.proxy.client.IUser.class.getMethod("expenses",
                    new Class[] {  });

            return ((java.lang.Double) this.invocationHandler.invoke(this, m,
                new Object[] {  })).doubleValue();
        } catch (Error ex) {
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }

        return 0.0;
    }

    public void shopping() {
        try {
            Method m = cn.ybzy.demo.proxy.client.IUser.class.getMethod("shopping",
                    new Class[] {  });
            this.invocationHandler.invoke(this, m, new Object[] {  });
        } catch (Error ex) {
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}


JDK 动态代理的实现机制是在运行时生成一个新的类,这个类实现了指定的接口,并且在这个新生成的类中,所有接口方法的实现都是转发到一个 InvocationHandlerinvoke 方法。

当你使用 Proxy.newProxyInstance 方法创建代理实例时,你需要提供:

  1. 一个类加载器(通常是被代理接口的类加载器)。
  2. 一个你希望代理实现的接口数组。
  3. 一个 InvocationHandler 实例,当代理对象的方法被调用时,实际上执行的是这个 InvocationHandlerinvoke 方法。

动态生成的代理类将实现你指定的所有接口,并且对这些接口中声明的所有方法的调用都将被转发到 InvocationHandler 实例。这意味着你可以在 invoke 方法中自定义这些方法调用的行为。

这里是一个简化的例子,展示了如何使用 JDK 动态代理:

import java.lang.reflect.*;

// 定义一个接口
public interface MyInterface {
    void doSomething();
}

// 实现该接口的一个类
public class MyInterfaceImpl implements MyInterface {
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

// 实现InvocationHandler接口的类
public 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 {
        // 方法调用前的处理
        System.out.println("Before method call");

        // 调用被代理对象的真实方法
        Object result = method.invoke(target, args);

        // 方法调用后的处理
        System.out.println("After method call");

        return result;
    }
}

// 创建代理实例的示例代码
public class ProxyExample {
    public static void main(String[] args) {
        MyInterface realObject = new MyInterfaceImpl();
        InvocationHandler handler = new MyInvocationHandler(realObject);

        MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class[] { MyInterface.class },
                handler
        );

        proxyInstance.doSomething(); // 调用代理实例上的方法
    }
}

在上面的代码中,proxyInstance 是一个实现了 MyInterface 接口的代理类实例。当你调用 proxyInstance.doSomething() 方法时,实际上会调用 MyInvocationHandlerinvoke 方法,并通过反射机制调用 MyInterfaceImpl 中的 doSomething 方法。这样,你就可以在不修改原始类 MyInterfaceImpl 的情况下,拦截和增强方法调用。

在上面的示例代码中,当你运行时,JDK 动态代理机制会在内存中生成一个新的代理类。这个类不会有一个可读的源代码文件,因为它是在运行时动态生成并直接加载到 JVM 中的。这个代理类会实现 MyInterface 接口,并将所有方法调用转发到 MyInvocationHandler 实例。

虽然我们无法直接查看这个类的源代码,但是我们可以推断它的结构。代理类大致上会遵循以下的模式:

public final class $Proxy0 extends Proxy implements MyInterface {
    private static Method m0;
    private static Method m1;
    // ... 其他方法

    static {
        try {
            m0 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Object.class });
            m1 = Class.forName("MyInterface").getMethod("doSomething", new Class[0]);
            // ... 初始化其他方法
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        } catch (ClassNotFoundException e) {
            throw new NoClassDefFoundError(e.getMessage());
        }
    }

    private InvocationHandler h;

    public $Proxy0(InvocationHandler h) {
        this.h = h;
    }

    public final boolean equals(Object obj) {
        try {
            return (Boolean) this.h.invoke(this, m0, new Object[] { obj });
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    public final void doSomething() {
        try {
            this.h.invoke(this, m1, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    // ... 其他方法实现
}

这个类的名字(比如 $Proxy0)是动态生成的,它会实现 MyInterface 接口,并且会在构造函数中接收一个 InvocationHandler 的实例。每个接口方法的实现都会调用 InvocationHandlerinvoke 方法,并传入当前的代理实例、对应的 Method 对象,以及方法参数。

请注意,这个类是动态生成的,所以你不会在你的项目中找到它的源代码。如果你想要查看或者分析代理类的字节码,你可以通过设置系统属性来保存生成的代理类:

System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

设置这个系统属性后,JVM 会将生成的代理类的字节码保存在工作目录下。这个功能在不同的 JDK 版本中可能有所不同,而且可能在未来的版本中不再支持。如果你想要了解更多细节,你可以使用反编译工具(如 JD-GUI)来查看这些 .class 文件的内容。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值