- 联系方式:1761430646@qq.com
- 菜狗摸索,有误勿喷,烦请联系
1. JDK Proxy 实现原理
-
前提:知道了 JDK Proxy 的代理对象,是通过Proxy类的静态方法
newProxyInstance
生成的 -
如下为
java.lang.reflect.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) { // 参数验证 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ // 寻找目标代理类缓存 或者 生成一个代理类 Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 获取生成的代理类对象的构造器对象 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } // 生成实例对象,也就是最终的目标代理对象 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
-
下面是这个
newProxyInstacne
方法的流程图 -
最最重要的是生成/获取 代理类类对象这一步,其他的就基本是反射的知识,下图为获得/生成代理类类对象的方法跳转流程
-
下面是
getProxyClass0(loader, intfs)
源码private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 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 return proxyClassCache.get(loader, interfaces); }
- 可以看到这个方法逻辑很简单,先是判断接口数组的长度不能大于 65535
- 然后再从代理缓存中根据
loader
和interfaces
获取代理对象实例- 如果能够根据
loader
和interfaces
找到代理对象,将会返回缓存中的对象副本 - 否则,它将通过
ProxyClassFactory
创建代理类
- 如果能够根据
-
在上述操作中,如果在代理缓存中找不到对应的代理对象,则会通过
ProxyClassFactory
类调用apply
方法创建- 从上图可知,代理类的类名前缀是通过变量
proxyClassNamePrefix
(固定的 - 代理类的类名后缀是通过变量
nextUniqueNumber
生成的数字固定的 - 所以我们见到的代理类类名通常是
@Proxy0
、@Proxy1
之类的
- 从上图可知,代理类的类名前缀是通过变量
-
在创建的过程中调用
apply
方法,主要干了如下几件事-
确定包名
- 如果不是公共的接口,则默认会使用
com.sum.proxy
作为代理类的包名
- 如果不是公共的接口,则默认会使用
-
确定全路径类名
- 可以看到,按照上面的包名以及前面的变量值拼接成
全路径类名
- 可以看到,按照上面的包名以及前面的变量值拼接成
-
然后,利用
ProxyGenerator.generateProxyClass
方法生成代理的字节码数组 -
最后,根据上面生成的
proxyClassFile
字节数组来生成对应的实例–注意这个实例可以通过设置参数来设为永久性的- 这个方法的核心内容就是生成 Class 文件,其作用主要以下
- 收集所有要生成的代理方法,将其包装成
ProxyMethod
对象并注册到 Map 集合中去 - 收集所有要为 Class 文件生成的字段信息和方法信息
- 完成了上面的工作后,开始组装为 Class 文件
- 收集所有要生成的代理方法,将其包装成
- 这个方法的核心内容就是生成 Class 文件,其作用主要以下
-
-
总结:JDK 为我们生成了一个叫
$Proxy0
,$Proxy1
之类的代理类,这个类文件放在内存中,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建代理实例
2. 重点
2.1 为什么 JDK 的动态代理一定基于接口的?
-
前提:
- 我们知道,要扩展一个类有常见的两种方式,继承父类或者是实现接口。
- 这两种方式都允许我们手动对方法的逻辑进行增强
-
简单点说:
- 但现在是由jvm去帮我们动态生成
- 而JDK选择最终生成的代理类是通过需要继承
Proxy
类来完成一些工作的 - 并且 Java 本身是只支持单继承
- 所以最终生成的代理类只能通过实现目标接口的方式来实现方法的扩展,达到我们增强目标方法的目的
2.2 动态代理实现方式
- 动态代理的常用实现方式是反射,反射机制是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用发射我们可以调用任意一个类对象,以及类对象中包含的属性及方法
- 但动态代理不知有反射一种实现方式,还可以通过 CGLib 来实现,而 CGLib 是基于 ASM(一个 Java 字节码操作框架) 而非反射实现的。
2.3 JDK Proxy 和 CGLib 的区别
- JDK Proxy 是 Java 自带的功能,无需加载第三方类实现,CGLib是第三方提供的工具,基于 ASM 实现的,性能比较高
- JDK Proxy 只能代理继承接口的类,而 CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的(也即代理类不能被
final
修饰的 - JDK Proxy 是通过拦截器加反射的方式实现的