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 动态代理的实现机制是在运行时生成一个新的类,这个类实现了指定的接口,并且在这个新生成的类中,所有接口方法的实现都是转发到一个 InvocationHandler
的 invoke
方法。
当你使用 Proxy.newProxyInstance
方法创建代理实例时,你需要提供:
- 一个类加载器(通常是被代理接口的类加载器)。
- 一个你希望代理实现的接口数组。
- 一个
InvocationHandler
实例,当代理对象的方法被调用时,实际上执行的是这个InvocationHandler
的invoke
方法。
动态生成的代理类将实现你指定的所有接口,并且对这些接口中声明的所有方法的调用都将被转发到 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()
方法时,实际上会调用 MyInvocationHandler
的 invoke
方法,并通过反射机制调用 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
的实例。每个接口方法的实现都会调用 InvocationHandler
的 invoke
方法,并传入当前的代理实例、对应的 Method
对象,以及方法参数。
请注意,这个类是动态生成的,所以你不会在你的项目中找到它的源代码。如果你想要查看或者分析代理类的字节码,你可以通过设置系统属性来保存生成的代理类:
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
设置这个系统属性后,JVM 会将生成的代理类的字节码保存在工作目录下。这个功能在不同的 JDK 版本中可能有所不同,而且可能在未来的版本中不再支持。如果你想要了解更多细节,你可以使用反编译工具(如 JD-GUI)来查看这些 .class
文件的内容。