JDK动态代理实现原理
JDK通过在运行时生成一个代理类实现动态代理。
//使用JDK生成代理对象,要定义的一个接口,代理对象也是此接口的实现类
interface HelloInterface {
void sayHello();
}
//被代理类实现接口
class HelloImpl implements HelloInterface {
@Override
public void sayHello() {
System.out.println("hello world");
}
}
//动态代理类,用于生成代理对象
class DynamicProxy implements InvocationHandler {
//本例中,被代理类是HelloImpl
Object originalObj;
//返回HelloImpl的代理对象$Proxy0.class的实例
Object getProxy(Object originalObj) {
this.originalObj = originalObj;
return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),
originalObj.getClass().getInterfaces(),
this);
}
//代理对象$Proxy0.class的实例运行sayHello(),会调用此方法,此方法切入了前置、后置代码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("切入前置代码,开始调用目标方法");
Object proxyObj = method.invoke(originalObj, args);
System.out.println("切入后置代码,目标方法已经调用结束");
return proxyObj;
}
}
public class J_DynamicProxyTest {
public static void main(String[] args) {
//在main方法中加入这句代码,程序运行时会在项目根目录生成一个名为$Proxy0.class的代理类Class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
HelloInterface helloProxy = (HelloInterface) new DynamicProxy().getProxy(new HelloImpl());
helloProxy.sayHello();
System.out.println(helloProxy instanceof HelloImpl); //false,helloProxy不是HelloImpl的实例
System.out.println(helloProxy.getClass().getName()); //com.jvm.proxy.$Proxy0
}
}
上面是一个JDK动态代理的简单例子。运行main()后会生成一个$Proxy0.class文件。
JDK并不会加载磁盘上的$Proxy0.class文件,事实上JVM是在内存中生成一个描述代理类的byte[]数组,加载的也是这个byte[]数组。通过System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");把byte[]数组写到磁盘中,只是为了读者更好的理解。
HelloInterface helloProxy = (HelloInterface) new DynamicProxy().getProxy(new HelloImpl());
helloProxy.sayHello();
System.out.println(helloProxy instanceof HelloImpl); //false,helloProxy并不是HelloImpl的实例
System.out.println(helloProxy.getClass().getName()); //com.jvm.proxy.$Proxy0
通过上面4行代码可以知道代理对象helloProxy并不是HelloImpl的实例,而是com.jvm.proxy.$Proxy0的实例。
直接用idea打开com.jvm.proxy.$Proxy0.class,主要源码如下:
//$Proxy0继承Proxy并实现HelloInterface
final class $Proxy0 extends Proxy implements HelloInterface {
private static Method m3; //sayHello方法
static {
try {
//m3是HelloInterface的sayHello方法
m3 = Class.forName("com.jvm.proxy.HelloInterface").getMethod("sayHello");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void sayHello() throws {
try {
/*
h是InvocationHandler的实现类,在本例中是DynamicProxy。
即helloProxy.sayHello();其实是执行DynamicProxy中的invoke方法
*/
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
$Proxy0.class的重点代码是这行 super.h.invoke(this, m3, (Object[])null);
打开$Proxy0父类Proxy的源码
可以看到h属性是InvocationHandler类型,在本例中就是DynamicProxy实例。
DynamicProxy实例是如何被设置到$Proxy0的h属性中的呢?看DynamicProxy中的getProxy方法
Object getProxy(Object originalObj) {
this.originalObj = originalObj;
return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),
originalObj.getClass().getInterfaces(),
this);
}
此方法调用了Proxy.newProxyInstance,并将this(DynamicProxy实例)作为参数传给newProxyInstance()。
看下Proxy.newProxyInstance的源码(仅列出主要部分):
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
final Class<?>[] intfs = interfaces.clone();
//获取com.jvm.proxy.$Proxy0的Class
Class<?> cl = getProxyClass0(loader, intfs);
// 获取com.jvm.proxy.$Proxy0的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
// 创建$Proxy0实例,并将在构造函数中执行this.h = h;
return cons.newInstance(new Object[]{h});
}
}
通过上面的代码,可以看出Proxy.newProxyInstance()返回的是$Proxy这个代理类的实例。
总结:
1、JDK实现动态代理,会生成$Proxy0.class。
class $Proxy0 extends Proxy implements HelloInterface
$Proxy0继承Proxy、实现HelloInterface
2、helloProxy = (HelloInterface) new DynamicProxy().getProxy(new HelloImpl());
helloProxy 是$Proxy0的实例。
$Proxy0中有一个属性h,h是DynamicProxy实例
3、helloProxy.sayHello()实际上是调用h.invoke()。h.invoke()中有我们切入的代码。