哈喽呀~~筒子们,小之最近在看Spring
的源码,正好遇到几个Aop
的问题涉及到Java的动态代理,以前对这个东西大体能理解,可是没有仔细的去看源码,今天咱们来扒一扒它的真面目。java
理解为何Jdk
动态代理,代理类必须实现接口git
理解Jdk
动态代理全过程github
看到Jdk
的动态代理类的类的内容 若是只想看这个能够直接翻到最后/(ㄒoㄒ)/算法
如何本身获得一个代理类的内容spring
如何看源码数组
变强缓存
public interface HelloService {
void sayHello();
}
/******************************************************/
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello() {
System.out.println("给掘金大佬们低头 (:");
}
}
/****************************************************/
public class HelloInvocationHandle implements InvocationHandler {
private Object target;
public HelloInvocationHandle(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy before=======");
Object result = method.invoke(target, args);
System.out.println("proxy end=======");
return result;
}
}
复制代码
上面的代码,相信你们在学动态代理的时候,都有写过,我就不赘述了,你们有想过他是怎么实现的吗?为何调代理类的sayHello()就能够直接使用InvocationHandler
里的invoke()
逻辑呢?为何代理类必需要实现一个接口这么麻烦呢?咱们继续往下面看。bash
咱们看上面的Main
方法,能够看出来Proxy.newProxyInstance()
这个方法承载了代理类的全部的逻辑,全部的魔法都在这里面网络
//生成一个代理对象
HelloService proxy = (HelloService) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
hello.getClass().getInterfaces(),handle);
复制代码
咱们点进去,看看里面长啥样数据结构
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
...
//这里的interface数组里面放的是 HelloService,就是代理类须要实现的那个接口
//这边复制了一份等待处理
final Class<?>[] intfs = interfaces.clone();
...
//获得了代理类的Class
Class<?> cl = getProxyClass0(loader, intfs);
...
//传入构造对象拿到代理类的构造器
//从这里咱们能够猜出这个代理类一个有一个构造方法是传入InvocationHandler进行初始化的
final Constructor<?> cons = cl.getConstructor(constructorParams);
//经过反射传入以前咱们定义的那个HelloInvocationHandle,进行构造器实例化代理对象
return cons.newInstance(new Object[]{h});
复制代码
简单的分析一下上面的代码,流程也很简单
HelloService.class
类进行代理类的生成,这样代理类就有了原始类的方法信息,例如sayHello()
InvocationHandler
这个构造对象拿到该代理类的构造方法HelloInvocationHandle
,构造器实例化了活生生的代理对象 从这里咱们大体就能明白了Jdk
动态代理的原理:克隆一个接口,生成一个新类做为代理类,这个类里面有着咱们定义的HelloInvocationHandle进行代理逻辑的处理这里就回答了咱们上面提到的一个问题: Jdk动态代理,代理类必须实现接口,是由于跟他的实现有关系,他规定了必需要传一个接口去生成代理类
因此全部的谜团就在生成代理类的getProxyClass0(loader, intfs)
这个方法里
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
...
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//这里从一个代理缓存的Cache中获得代理,Cache里面涉及虚引用实引用的东西不在咱们讨论的范围里,咱们直接看这个类是怎么生成的
return proxyClassCache.get(loader, interfaces);
}
//从上面的注释和这里咱们均可以看出代理类是从一个叫ProxyClassFactory里生成的
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
复制代码
看上面的注释咱们能够看出到ProxyClassFactory
负责生成代理类
我这里看的是jdk1.8
的源码,用lambda
重构过,旧版本看到的能够不同,主逻辑应该没什么变化
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
//这里定义了代理类的名字,因此你每次看到的代理类都是$Proxy开头的
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//关键点在这里,这里生成一个代理类
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
}
复制代码
ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)
这个方法若是感兴趣的大佬点进去能够看到,里面是用StringBuilder
拼出了这个代理类的字节码(:,而后转成了Byte
数组。
关键点来了! 这个代理类长啥样怎么看?Debug
没有暴露出来啊亲,看不了,小之看到上面的byte[] proxyClassFile
这个变量,露出了一丝坏笑,前面说过,这个变量里存的是代理类字节码内容啊!没错!输出流伺候! 小之一顿操做,QWER!飞起
public final class $Proxy0 extends Proxy implements HelloService {
private static Method m1; //equals()方法
private static Method m3; //Bingo 咱们的sayHello方法()
private static Method m2; //toString()方法
private static Method m0; //hashCode()方法
//这里就是咱们以前提交的InvocationHandler这个构造方法!!
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})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
//勇士来吧!看一看咱们的代理类的sayHello()方法长什么样子呀!!
public final void sayHello() 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 int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m3 = Class.forName("proxy.service.HelloService").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
复制代码
筒子们,请聚焦上面的代理类sayHello()
方法。真相大白了吧?代理类里实现了接口里的方法,所有调用了你的HelloInvocation
的invoke()
方法啦!
这里说一下上面的super.h.invoke(this, m3, (Object[])null);
中super.h
是什么东西,明眼的大佬都应该看出来了吧?继承了Proxy类,是Proxy
里定义的InvocationHandler
变量,也就是你的HelloInvocation
大佬们,是否是以为Jdk
动态代理也不是很复杂?好像本身也能写出来?哈哈,本篇文章还想提供一个Debug
代码的思路,其实看源码没那么难,但愿能够帮到你们,咱们下次再见啦~
对了我这里安利一下个人Github
:点我点我 目前正在作的几个东西:
Triple 本身写的一个RPC
框架,练手的,功能不是很完善,主要目的是为了借助这个去理解网络相关的知识点,如Netty
,想起来写一点~
数据结构和算法 数据结构和算法是薄弱项,多学学练练好吹吹🐂🍺呀
Spring源码分析 还在分析,里面会整理一些问题,带着问题去看源码效率更高一点,欢迎你们有什么关于Spring
的不懂的,能够给我提Issue
,文章写好了应该也会发到掘金,毕竟基佬多(逃。