动态代理源码解析
动态代理是什么?
动态代理是一种强大的功能,它可以在运行时动态创建一个类,实现一个或多个接口,可以在不修改原有类的基础上动态为通过该类获取的对象添加方法、修改行为,这么描述比较抽象,但是它实际用在许多的开源框架之中像Spring, Mybtis, Hibernate,等
动态代理剖析
既然这个在运行时动态创建一个类,实现一个或多个接口,那我们就把这个类导出来解析下
- 对于动态代理(JDK)的写法就不在赘述了,下面是一个很简单的Demo,就是为了引出代理类的:
/**
* <Description> <br>
*
* @author shi.yuwen<br>
* @version 1.0<br>
* @see proxy2 <br>
*/
public class ProxyTest {
interface Animal {
void play ();
}
class Dog implements Animal {
@Override
public void play() {
System.out.println("狗狗玩飞碟!");
}
}
static class ProxyFactory {
public static <T> T getProxy(Object o) {
return (T) Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行前调用");
Object result = method.invoke(o, null);
System.out.println("执行后调用");
return result;
}
});
}
}
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
Animal dogProxy = (Animal)ProxyFactory.getProxy(new ProxyTest().new Dog());
dogProxy.play();
}
}
在执行main()前配置Run Configurations,加上java命令(这边jdk版本为1.801 高版本jdk这个参数可能会失效)
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
在根目下会把内存中的代理类输出出来:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy2.ProxyTest.Animal;
final class $Proxy0 extends Proxy implements Animal {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void play() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("proxy2.ProxyTest$Animal").getMethod("play");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
- 首先看这个生成的代理类:继承了Proxy类实现了我们传入的接口(Animal)所以这就是为什么代理类能被接口接受的原因(多态)
- 往下是4个成员变量(Method)对于熟悉反射的人来说很容易理解,是什么时候赋值的呢,在这个类的最下面的静态代码块中(在装载类的时候变实现了初始化赋值)其中m1,m2,m4是Object的方法,m3是我们接口定义的方法play()
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("proxy2.ProxyTest$Animal").getMethod("play");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
- 再往先看是一个构造方法它直接调用了父类构造方法,这边直接解释下:就是直接把我们写的InvocationHandler传入进来(就是Proxy.newProxyInstance的第三个参数)
- 往下equals(),toString()就不解释了,play()方法(由于实现了Animal接口)下面是这个代理类的核心代码:
super.h.invoke(this, m3, (Object[])null);
- 我们main()方法中代理类调用play方法会执行super.h.invoke方法上面也说过父类中已经有我们写的InvocationHandler 的实例了,那么便是执行我们自己重写的invoke(),传递三个参数this, m3, (Object[])null),便传递到了下面的参数
invoke(Object proxy, Method method, Object[] args)
- 所以代理类执行play()就会转而执行InvocationHandler的invoke(),而我们在invoke()中写的method.invoke()便会执行被代理对象的方法,所以在method.invoke()的上下文执行写before(),after(),便是相当于方法的增强.
这样已解析动态代理其实是很通俗易懂的,事实上也是如此代理模式确实算不上较难的设计模式
小结
本文解析了动态代理(基于JDK)的代理类的源码,像动态代理还有一种CGLib会在之后解析