源码阅读JDK动态代理底层实现
上次更新了一篇设计模式之代理模式,今天仔细的了解了一下JDK实现的原理,又看了一下网上其实很多人都说自己也能实现一个代理模式,手痒就敲了一下。
想要学习完整代理模式的可以看我前几篇更新的文章:
https://www.jianshu.com/p/bee8a547faa3
动态代理主要有两种,一种是JDk提供的,代理类需要实现InvocationHandle接口,然后内部在构造方法去返回一个Proxy生成的代理类实例,然后还要重新invoke这个方法,在这个方法中实现代理类想要做的事。
在客户端获取这个代理类之后,直接调用方法就可以了。
另外一种就是通过CGLib实现的。这个我下次再来研究,这次是实现的JDK的动态代理模式。
可以先来贴一段代码(按注释编号读):
//1.这是一个被代理类
public class XieMu implements Person {
@Override
public void findLove(){
System.out.println("高富帅");
System.out.println("身高180cm");
System.out.println("胸大,6块腹肌");
}
@Override
public void zufangzi() {
System.out.println("租房子");
}
@Override
public void buy() {
System.out.println("买东西");
}
@Override
public void findJob() {
System.out.println("月薪20K-50k");
System.out.println("找工作");
}
}
// 2.这是一个媒婆的代理类
public class JDKMeipo implements InvocationHandler{
//被代理的对象,把引用给保存下来
private Person target;
public Object getInstance(Person target) throws Exception{
this.target = target;
Class<?> clazz = target.getClass();
//下半截,深入底层来给大家讲解字节码是如何重组的
//用来生成一个新的对象(字节码重组来实现)
/**
* JDK提供了一个Proxy.newProxyInstance(代理类的类加载器,代理类实现的接口,代理类本身)我们直接调用,他就会帮我们在运行时动态生成一个代理类。
*/
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
//他的客户端实现如下:
//3可以看到,通过媒婆这个类返回了一个代理类,这个类跟被代理类实现了同样的接口Person,返回Person
Person obj = (Person)new JDKMeipo().getInstance(new XieMu());
System.out.println(obj.getClass());
obj.findJob();
}
可以看到,客户端只需要通过这个媒婆类的getInstance,就可以返回一个我们被代理类实现的接口的代理类。然后我们深入Proxy.newProxyInstance来看一下:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
//浅复制了被代理类实现的接口
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
//检查创建Proxy类所需的权限
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 查找或生成制定的代理类
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
*使用指定的调用处理程序调用其构造函数
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//重点来了,反射获取constructorParams,也就是InvocationHandler.class类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h; //看了上下文,发现没意义。
//获取此类cl,即代理类的java语言修饰符,判断如果不是public的访问域,则设置可以setAccessible(true);访问
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//以上的设置,是为了下面代理类的构造器新生成一个代理类实例,通过反射去生成
return cons.newInstance(new Object[]{h});
然后接这cons.newInstance(new Object[]{h});继续看:
可以通过双亲委派模式,去判断,最终是这里实现了新建一个对象。
return UnsafeFieldAccessorImpl.unsafe.allocateInstance(this.constructor.getDeclaringClass());
然后来看,那么我们实现InvocationHandle,重新invoke跟这个Proxy有什么关联呢? 再来看一下。Proxy类有一个这样的注释:
An interface method invocation on a proxy instance will be
* encoded and dispatched to the invocation handler's {@link
* InvocationHandler#invoke invoke} method as described in the
* documentation for that method
代理实例上的接口方法调用将被编码并调度到调用处理程序的{@link * InvocationHandler#invoke invoke}方法,如该方法的*文档中所述
最后我们再来看一下,通过Proxy生成的代理类打印出来是什么?
class com.sun.proxy.$Proxy0 //JDK中有个规范,只要要是$开头的一般都是自动生成的,所以肯定是Proxy自动生成一个代理类后,再加载到JVM去运行的。
//通过反编译工具可以查看源代码
byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
os.write(bytes);
os.close();
查看到的代码如下:
import com.zxy.test.gupao.tom.proxy.staticed.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m6;
private static Method m4;
private static Method m5;
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 void findLove() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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 zufangzi() throws {
try {
super.h.invoke(this, m6, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void buy() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void findJob() throws {
try {
super.h.invoke(this, m5, (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"));
m3 = Class.forName("com.zxy.test.gupao.tom.proxy.staticed.Person").getMethod("findLove");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m6 = Class.forName("com.zxy.test.gupao.tom.proxy.staticed.Person").getMethod("zufangzi");
m4 = Class.forName("com.zxy.test.gupao.tom.proxy.staticed.Person").getMethod("buy");
m5 = Class.forName("com.zxy.test.gupao.tom.proxy.staticed.Person").getMethod("findJob");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
以下是我总结的JDK底层实现JDK的原理:
//原理:
//1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取
//2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口
//3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
//4、编译新生成的Java代码.class
//5、再重新加载到JVM中运行
//以上这个过程就叫字节码重组
好了,以上我已经总结了JDK底层是如何实现动态代理的了,那么下面我将自己来实现JDK动态代理。
可以看我的下一篇博客哦:
地址:
https://www.jianshu.com/p/9608c628f823
感谢您阅读我的文章,如果满意可以帮我点赞,谢谢哈。
如果对文章部分还有什么见解或者疑惑,可以私信评论我,欢迎技术讨论。如果需要获取完整的文件资源,可以加我微信z985085305,获取我整理的全套笔记。
思想的碰撞最能促进技术的进步哦。