学一个技术,要知道技术因何而产生,才能有学下去的目标和动力,才能更好的理解。
本文从为什么要有代理说起:
- 讲解了代理的种类
- 添加动静态代理实例
- 从源码角度分析了动态代理的实现
学一个技术,要知道技术因何而产生,才能有学下去的目标和动力,才能更好的理解
首先,要明确为什么要存在代理
呢?
存在一个常见的需求:怎样在不修改类 A 代码的情况下,在调用类 A 的方法时进行一些功能的附加与增强呢?
先不考虑什么代理不代理的,我们设计一个简单的实现方案:
新创建一个类 B,类 B 组合类 A,在类 B 中创建一个方法 b,方法 b 中调用类 A 中的方法 a,在调用前和调用后都可以添加一些自定义的附加与增强代码。 当有需求需要调用类 A 的方法 a 并且想要添加一个附加功能时,就去调用类 B 的方法 b 即可实现上述需求;
下面为了便于理解,附上伪代码:
// 定义类 Apublic class ClassA{ public void methoda(){ System.out.println("我是方法 a!"); }}// 定义类 Bpublic class ClassB{ // 组合 ClassA ClassA A; public ClassB(ClassA A){ this.A = A; } @Override public void methodb(){ System.out.println("我是方法 a 的附加功能代码,我执行啦~!"); A.methoda(); System.out.println("我是方法 a 的附加功能代码,我完成啦~!"); }}
下面,让我们来调用一下 ClassB 的 methodb 方法,则会产生以下输出:
我是方法 a 的附加功能代码,我执行啦~!我是方法 a!我是方法 a 的附加功能代码,我完成啦~!
可以发现,方法 a 执行了,并且在没有修改类 A 代码的前提下,为方法 a 附加了其他的功能;不难吧,其实上述的代码就是一个最简单的代理模式
了
代理存在的意义:使用代理模式可以在不修改别代理对象代码的基础上,通过扩展代理类,进行一些功能的附加与增强
代理种类代理分为静态代理
和动态代理
,其涉及的设计模式就是代理模式
本尊了,代理模式一般包含几种元素,如下图:
- 主题接口(subject):定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;
- 真实主题(RealSubject):真正实现业务逻辑的类;
- 代理类(Proxy):用来代理和封装真实主题;
- 客户端(Client):使用代理类和主题接口完成一些工作。
为了更好的理解,我们将上述实现的最简易版的代理完善一下,添加接口,代理类也实现相应的被代理类的接口,实现同一个方法,伪代码如下:
// 被代理类的接口(上图中 subject)public interface ImpA{ void methoda();}// 定义类 A(上图中 RealSubject)public class ClassA implements ImpA{ public void methoda(){ System.out.println("我是方法 a!"); }}// 定义类 B(上图中 Proxy)public class ClassB implements ImpA { // 组合 ClassA ImpA A; public ClassB(ClassA A){ this.A = A; } // 重写被代理类的方法 @Override public void methoda(){ System.out.println("我是方法 a 的附加功能代码,我执行啦~!"); A.methoda(); System.out.println("我是方法 a 的附加功能代码,我完成啦~!"); }}// 客户端类(上图中 Client)public class Main{ // 创建被代理对象实例 ImpA A = new ClassA(); // 构造器注入被代理对象实例 ImpA B = new ClassB(A); // 调用代理方法 B.methoda();}
静态代理
所谓静态代理也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
上面的代码就是实现了一个静态代理; 其实静态代理就已经能够满足上述需求了,为什么还需要动态代理呢? 这里就涉及到静态代理的两个缺点了
代理对象
的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,在程序规模稍大时静态代理代理类就会过多会造成代码混乱- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度。基于上述两个问题,动态代理诞生了~
动态代理是在程序运行时,通过反射获取被代理类的字节码内容用来创建代理类
具体什么是动态代理呢?名词:动态
,动态在程序中就是表达在程序运行时就根据配置自动的生成代理类并且代理类和被代理类是在运行时才确定相互之间的关系;
在 JDK 中包含两种动态代理的实现机制:JDK Proxy
和 CGLib
;
下面我们以JDK Proxy
为例,讲解一下动态代理和根据源码分析并简单说一下应用场景
JDK Proxy 动态代理
,api 在包java.lang.reflect
下,大家可能发现了,为什么在反射的包下呢?这个问题我们下面的源码分析会解决;
其核心 api 包含两个重要的核心接口和类:一个是 InvocationHandler(Interface)
、另一个则是 Proxy(Class)
,简单说就这两个简单的很,这两个是我们实现动态代理所必需的用到的,下面简单介绍一下两个类:java.lang.reflect.Proxy(Class)
:Proxy 是 Java 动态代理机制的主类,提供一组静态方法来为一组接口动态地生成代理类及其对象。包含以下四个静态方法:
static InvocationHandler getInvocationHandler(Object proxy)
该方法用于获取指定代理对象所关联的调用处理器static Class getProxyClass(ClassLoader loader, Class[] interfaces)
该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象static boolean isProxyClass(Class cl)
该方法用于判断指定类对象是否是一个动态代理类static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)
该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例,包含下面的参数:loader
指定代理类的 ClassLoader 加载器interfaces
指定代理类要实现的所有接口h
: 表示的是当这个动态代理对象在调用方法的时候,会关联到哪一个 InvocationHandler 对象上
该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
java.lang.reflect.InvocationHandler(interface)
: InvocationHandler
是上述newProxyInstance
方法的InvocationHandler h
参数传入,负责连接代理类和委托类的中间类必须实现的接口它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
上述就是动态代理两个核心的方法,不太明白?先别急,我们先用上述实现一个动态代理,你先看一下
还是以上述的案例从静态代理来改造为动态代理,实现动态代理主要就两步,假设还是存在上述的 ImplA、ClassA
1:创建一个处理器
类实现 InvocationHandler 接口,重写 invoke 方法,伪代码如下:
public class MyHandler implements InvocationHandler{ // 标识被代理类的实例对象 private Object delegate; // 构造器注入被代理对象 public MyHandler(Object delegate){ this.delegate = delegate; } // 重写 invoke 方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("被代理方法调用前的附加代码执行~ "); // 真实的被代理方法调用 method.invoke(delegate, args); System.out.println("被代理方法调用后的附加代码执行~ "); } }
好了,这样一个处理器
就搞定了,当我们在调用被代理类
的方法时,就是去执行上述重写的 invoke 方法
,下面创建一个 ClassA 的代理类
2:创建代理类
,并调用被代理方法
public class MainTest{ public static void main(String[] args) { // 创建被代理对象 ImplA A = new ClassA(); // 创建处理器类实现 InvocationHandler myHandler = new MyHandler(A); // 重点! 生成代理类, 其中 proxyA 就是 A 的代理类了 ImplA proxyA = (ImplA)Proxy.newProxyInstance(A.getClass().getClassLoader(), A.getClass().getInterfaces(), myHandler); // 调用代理类的代理的 methoda 方法, 在此处就会去调用上述 myHandler 的 invoke 方法区执行,至于为什么,先留着疑问,下面会说清楚~ proxyA.methoda(); }}
好了,至此一个动态代理就构建完成了,执行代码,会发现输出:
被代理方法调用前的附加代码执行~我是方法 a!被代理方法调用后的附加代码执行~
太简单了有木有,这里总结一下动态代理的优缺点:
优点:
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为 Java 反射机制可以生成任意类型的动态代理类。
动态代理类的字节码在程序运行时由 Java 反射机制动态生成,无需程序员手工编写它的源代码。
接口增加一个方法,除了所有实现类需要实现这个方法外,动态代理类会直接自动生成对应的代理方法。
缺点:JDK proxy 只能对有实现接口的类才能代理,也就是说没有接口实现的类,jdk proxy 是无法代理的,为什么呢?下面会解答.
有什么解决方案吗? 当然有,还有一种动态代理的方案:CGLib
,它是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成 final,对于 final 类或方法,是无法继承的,和 jdk proxy 基本思想是相似的,毕竟都是动态代理的实现方案嘛,在这篇文章就不做详解了,博主会在其他的博文单独介绍这个 nb 的框架
上述带大家搞了一遍动态代理和静态代理的应用;在这过程中,你有没有想过,动态代理是怎么实现的呢?
下面我们就从源码的角度分析一下,解决大家的疑问。
源码分析在开始分析的时候,我希望大家带着几个问题
去阅读,可以帮助大家更好的理解:
问题 1
:代理类为什么可以在运行的时候自动生成呢?如何生成的呢?问题 2
:为什么调用代理类的相应的代理方法就可以调用到 InvocationHandler 实现类的 invoke 方法呢?问题 3
:为什么 jdk proxy 只支持代理有接口实现的类呢?
ps :为了提升阅读体验,让大家有一个更清晰的认知,以下源码会将一些异常处理和日志打印代码删除,只保留主干代码,请知悉~
我们就从两个核心:InvocationHandler
和Proxy
来进行分析整个脉络,他们都在java.lang.reflect
包下
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}
上述就是 InvocationHandler 的源码,没什么其他的就是一个接口,里面有一个待实现方法 invoke,处理类实现此接口重写 invoke 方法
Proxy 源码public class Proxy implements java.io.Serializable { // 处理类实例 变量 protected InvocationHandler h; // 用于存储 已经通过动态代理获取过的代理类缓存 private static final WeakCache> proxyClassCache = new WeakCache(new KeyFactory(),new ProxyClassFactory()); // 私有无参构造,使得只能通过传入 InvocationHandler 参数来创建该对象 private Proxy() {} // 保护 构造函数,入参 InvocationHandler 处理类 protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; } public static Class getProxyClass(ClassLoader loader,Class... interfaces) throws IllegalArgumentException{ ... } public static boolean isProxyClass(Class cl) { ... } public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { ... } // 生成代理类的实现方法 public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h) throws IllegalArgumentException{ ... } // 各种私有方法 private ... ...}
Proxy
类的整体的架构就类似于上述,InvocationHandler h
参数和两个构造函数
、四个上述已经介绍过的共有方法
,还有一系列的私有方法,getProxyClass、isProxyClass、getInvocationHandler 功能就和上面介绍的一样,就不再详细介绍了
我们下面来主要看一下newProxyInstance
方法newProxyInstance 方法,我在方法内添加上了对应的注释:
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException{ // 1. 克隆对应的接口,用于代理类实现的接口数组 final Class[] intfs = interfaces.clone(); ... /* * Look up or generate the designated proxy class. 源码中的介绍 * 2. 查找或者生成指定的代理类, 下面会详细介绍 */ Class cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. * 3. 上面代码已经生成了代理类 cl,cl 其中包含一个参数为传入的 InvocationHandler h 的构造函数, 获取该构造函数并通过该构造函数创建一个类的实例对象并返回 */ try { // 4. 通过《反射》获取参数为 InvocationHandler 的构造函数 final Constructor cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; // 5. 判断构造函数是否为私有的,如果为私有的则需要设置私有可访问权限 if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } // 6. 通过上述获取的构造函数创建对应的 实例对象,并返回!over~ return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { // 各种异常处理 } }
在上面的代码中,我简单的标注了一下每行代码的作用,下面我们来详细分析一下;
代理类的字节码生成逻辑
我们知道,在加载 jvm 前,java 文件都已经被编译成了class 字节码
文件, 然后 jvm 通过类加载器
将字节码文件加载到 jvm 中;
我们的代理类也是这样,不同的是动态代理的类是在程序运行时产生的,我们要做的就是如何在程序运行的时候,通过被代理类
的字节码生成代理类
的字节码!
我们接下来详细分析newProxyInstance
方法:
在 newProxyInstance 中调用了Class cl = getProxyClass0(loader, intfs);
语句生成了代理类的字节码
,此处调用了 getProxyClass0 方法,传入了指定的类加载器和对应要实现的接口
那么, 我们看看getProxyClass0
方法的实现:
private static Class getProxyClass0(ClassLoader loader, Class... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // proxyClassCache 是 WeakCache 弱引用缓存类,如果之前就生成过对应的代理类就从缓存中取,如果没生成过就重新生成 return proxyClassCache.get(loader, interfaces); }
proxyClassCache
是 Proxy 类中的静态变量,是 WeakCache 类,里面封装了两个类 KeyFactory、ProxyClassFactory,都是 BiFunction 函数式接口(如果不清楚函数式接口,请自行 google);
将其拿过来看一下proxyClassCache = new WeakCache(new KeyFactory(),new ProxyClassFactory());
其中调用了 proxyClassCache.get(loader, interfaces)方法的实现
未避免代码过长,只粘贴了核心代码:
public V get(K key, P parameter) { ... // 这部分主要是获取对应的 函数式接口,如果不明白函数式接口,google 一下吧~ Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); Supplier supplier = valuesMap.get(subKey); Factory factory = null; while (true) { // 此处为什么是 while 循环呢, 主要是 supplier 不为空的话,则执行下面的语句赋值后,再循环执行下一次则 supplier 不为空 if (supplier != null) { // 如果存在对应的函数式接口, 调用函数式接口对应的代码 // 重点!!!调用函数式接口!! V value = supplier.get(); if (value != null) { return value; } } if (factory == null) { // 创建一个 专门创建代理类字节码的工厂类,实现类是 ProxyClassFactory factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // 将 supplier 赋值 factory supplier = factory; }}}} }
总结一下上述方法的流程:接着
ProxyClassFactory.apply
方法看一下:
public Class apply(ClassLoader loader, Class[] interfaces) { Map interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { } } String proxyPkg = null; int accessFlags = Modifier.PUBLIC | Modifier.FINAL; // 判断是否包含公有的接口对象,判断是否可以通过 jdk proxy 的方式进行生成代理类 for (Class intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { } } } // 如果没有公有接口类,需要使用 CGLib 来实现。。。 if (proxyPkg == null) { proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } // 组装代理类的类名称 long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; // 重点!! 此处生成代理类的字节码数组 byte[] proxyClassFile = ProxyGenerator.generateProxyClassproxyName, interfaces, accessFlags); try { // 通过类加载器将字节码数组加载到 JVm 的方法区中生成 Class 对象! return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { } }
上述的byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
为生成代理类字节码数组的方法,调用的方法中调用了generateClassFile
方法;
private byte[] generateClassFile() { // 首先,默认代理的三个方法:hashCode\equals\toString this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); // 获取所有的要被代理类实现的接口 Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; // 遍历上述获取的接口 for(var3 = 0; var3 < var2; ++var3) { // 赋值: 将接口的 Class 对象赋值! var4 = var1[var3]; // 通过“反射”获取所有方法 Method[] var5 = var4.getMethods(); int var6 = var5.length; for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; // 将方法添加到 要被代理的方法中 this.addProxyMethod(var8, var4); } } // 获取要代理方法后,开始组装字节码 var14.writeInt(-889275714); var14.writeShort(0); var14.writeShort(this.accessFlags); var14.writeShort(this.cp.getClass(dotToSlash(this.className))); // 注意!!! .... 此处省略了绝大部分 字节码的组装过程,只给出了几行代码展示一下字节码的组装 // 最终返回组装好的字节码文件 return var13.toByteArray(); } }
在generateClassFile
中,你会发现里面全部是重组字节码的代码, 主要是获取被代理类
字节码和操作类 InvocationHandler
字节码组装出代理类
的字节码,在重组的过程因为是在运行时
进行了代理类的创建,无法像往常一样 new 一个被代理类的实例获取他的方法,让代理类进行调用。
获取字节码后,接下来就要将代理类的字节码加载进 JVM 中了,这里调用的是一个return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length)
其中的defineClass0
是一个本地 native 方法,传入了代理类名称、类加载器、代理类的字节码文件、文件长度参数
,从而将字节码加载进 JVM 中! 代码如下:
private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
将代理类的字节码加载进 JVM 后,会在方法区内生成一个Class 对象
,标识这个代理类;
代理类的实例生成逻辑
上面,我们知道了通过字节码技术生成了代理类字节码,并通过类加载器将字节码文件加载到了 JVM 的方法区中生成了一个 Class 对象,我们如何在运行时获取这个 Class 对象的实例呢? 只有获取了对象实例才可以使用不是~还是回到newProxyInstance
方法中,上面我们分析了Class cl = getProxyClass0(loader, intfs)
这部分逻辑,生成了 Class 对象cl
,下面生辰该实例代码,过程很简单,相关逻辑我就直接在代码中注释了
// 定义构造函数的参数类型,下面的一个语句使用 private static final Class[] constructorParams = { InvocationHandler.class }; // 通过反射获取上述获取的 Class 对象的带参构造函数,参数必须是上述定义的 InvocationHandler.class 类型 final Constructor cons = cl.getConstructor(constructorParams); // 检查权限,如果是私有权限,设为可被访问 if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } // 通过构造函数传入对应 处理类 h 参数,生成实例! return cons.newInstance(new Object[]{h});
上述就是生成实例的代码,生成实例后newProxyInstance
就返回该实例了,就可以使用了~
那如何才能在运行时获取到被代理类的构造函数、方法、属性等字节码呢? 此时“反射!
”登场了!我们通过反射可以在运行时获取到类的所有信息,所有哦。
定义: JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java 语言的反射机制
。
比如,在上述所说的组装代理类字节码时,在获取被代理类的所有方法时,就调用了Method[] var5 = var4.getMethods();
反射中的getMethods
方法,通过反射获取到了被代理类的所有方法,这样我们就可以在运行时获取到任何类的所有的字节码信息了! 从而可以组装出我们想要的代理类字节码!
所以说,反射
也为动态代理
的实现提供了理论支持
!!因为只有在运行时能获取到对应类的信息,才可以通过信息创造出对应的我们所需要的代理类;
总而言之,动态代理
的理论支持是可以通过反射机制
在运行时
获取到类的所有信息,如果运行时获取不到被代理类的信息,那还咋生成代理类。
动态代理的大致流程:
通过上述流程。我们就获得了一个代理类对象了,调用代理类对应的方法,就会执行我们规定的执行逻辑,实现对被代理类的运行时动态增强和扩展!
此时,我们再拿出刚开始我们用 JDK proxy 实现的动态代理代码中的生成代理类的代码:ImplA proxyA = (ImplA)Proxy.newProxyInstance(A.getClass().getClassLoader(), A.getClass().getInterfaces(), myHandler)
每个参数的作用,是不是就很清晰了
上面动态代理实现流程
,我们可以回答上述的第一个代理类为什么可以在运行的时候自动生成呢?如何生成的呢?
问题了
对于第二个为什么调用代理类的相应的代理方法就可以调用到 InvocationHandler 实现类的 invoke 方法呢?
和第三个为什么 jdk proxy 只支持代理有接口实现的类呢?
问题,我们需要反编译一下我们通过字节码技术产生的代理类
,如下:
final class $Proxy0 extends Proxy implements ImplA { private static Method m1; private static Method m3; private static Method m2; private static Method m0; static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.test.ImplA").getMethod("methoda"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { // .. } } public $Proxy0(InvocationHandler var1) throws { super(var1); } // 需要被加强的方法 methoda public final void methoda() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final boolean equals(Object var1) throws { // 省略部分代码。。。 return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } public final String toString() throws { // 省略部分代码。。。 return (String)super.h.invoke(this, m2, (Object[])null); } public final int hashCode() throws { // 省略部分代码。。。 return (Integer)super.h.invoke(this, m0, (Object[])null); }}
上述代码包含几个关键点:
- 方法为
final
类型,不可再被继承 - 代理名称为
$Proxy
代理类前缀 + 递增数字 - 继承动态代理的核心类
Proxy
, 实现了我们指定的接口ImplA
- 一个带参构造方法
$Proxy0(InvocationHandler var1)
传入 InvocationHandler 内部调用了父类的Proxy
的构造函数 - methoda、toString、hashCode、equals 全部调用的传入的 InvocationHandler 参数的
invoke
方法!!!
现在回答第二个问题为什么调用代理类的相应的代理方法就可以调用到 InvocationHandler 实现类的 invoke 方法呢?
显而易见,代理类内部的代理方法全部显式调用的 InvocationHandler 实现类的 invoke 方法
第三个问题为什么 jdk proxy 只支持代理有接口实现的类呢?
因为代理类在使用 JDK proxy 方式生成代理类时,默认继承 Proxy 类,又因为 java 语言是单继承不支持多继承,那怎样才能标识我要代理什么类型的类或是代理什么方法呢? 接口呗,java 支持接口的多继承,多少个都 ok~
好了,上述将动态代理的使用方式 和 实现原理统一过了一遍,也回答了几个容易疑惑的问题,下面我们简单说下动态代理在现实的 java 框架大家庭中的一些典型应用
动态代理的应用spring aop
: 这可以说是 spring 框架中最典型的应用了,通过动态代理在运行时产生代理类,完成对被代理类的增强和功能附加
RPC 框架的实现
: 远程过程调用,RPC 使得调用远程方法和调用本地方法一样,这是怎么搞的呢?服务方对外放出服务的接口 api,调用方拿到接口 api,通过动态代理的方式生成一个代理类,代理类的处理类的 invoke 方法可以通过 websocket 连接远程服务器调用对应的远程接口; 这样我们再用代理对象进行调用对应方法时时,就像调用本地方法一样了
mybatis 框架中
: mapper.xml 中编写 sql 语句,mapper.java 接口写上对应的方法签名;我们直接调用 mapper.java 中的方法就可以执行对应的 sql 语句,有没有想过为什么? 框架使用动态代理创建一个 mapper.java 的代理对象,代理对象的处理类 invoke 中执行 sql,就 ok 了
代理分为静态代理
和动态代理
,动态代理的两种实现方式:JDK Proxy
和CGLib
,动态代理的核心反射机制
,通过反射在运行时获取被代理类字节码和处理类字节码,动态代理代理类的生成通过重组字节码
的方式。
原创不易,有收获的话,关注
、点赞
、评论
三连支持,我最大的动力~
关于博文有任何问题请不吝评论,感谢
参考:JDK 源码,https://www.jianshu.com/p/861223789d53
阅读全文: http://gitbook.cn/gitchat/activity/5ec75dcbd2aed541d53eb027
您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。