保姆级手把手捋动态代理流程(JDK+Cglib超详细源码分析)
Posted AQin1012
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了保姆级手把手捋动态代理流程(JDK+Cglib超详细源码分析)相关的知识,希望对你有一定的参考价值。
简介
动态代理,通俗点说就是:无需声明式的创建java代理类,而是在运行过程中生成"虚拟"的代理类,被ClassLoader加载。 从而避免了静态代理那样需要声明大量的代理类。
上面的简介中提到了两个关键的名词:“静态代理”和“动态代理”
我们先来看来下两个问题:
首先什么是代理呢?
它可以看作是对最终调用目标的一个封装,可以通过操作代理对象来调用目标类,这样就可以实现调用者和目标对象的解耦合
“静态代理”和“动态代理”又有什么区别呢?
这是代理模式的两种类型,Java中(JDK从1.3版本开始支持动态代理)主要是通过java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler这两个类配合使用来实现的,而所谓的“静态”和“动态”其实也就是代理类的字节码文件创建的时间不同,“静态代理”是在程序运行前就知道了哪些对象需要创建代理对象,“动态代理”则是在程序运行中动态的判断哪些对象需要创建代理对象
Java中主要有两种"动态代理"的实现方式:JDK和CGLIB
他们最主要的区别简单来讲就是
JDK动态代理针对实现了接口的类生成代理(必须实现接口)
CGLIB动态代理是针对类实现代理(无需实现接口)
动态代理这个东西吧。。。个人的感觉是不自己走一遍流程,看别人讲再多遍,总是不踏实,本系列就是一套保姆级的Debug教程,接下来会通过两个示例🌰,跟我一起手把手地捋下JDK动态代理和CGLIB动态代理创建代理对象的基本流程
JDK
这个案例我们一共需要创建4个文件📃
创建测试文件
IService:接口文件
![](https://image.cha138.com/20230329/67dae8cdcfe04d1fbc85fc4e2da03a99.jpg)
public interface IService
void A();
String B(int i);
MyService:实现类(被代理类)
![](https://image.cha138.com/20230329/576fe6a87d2548fcb4e6ef69b5299c92.jpg)
public class MyService implements IService
@Override
public void A()
System.out.println("This is A().");
@Override
public String B(int i)
return "This is B()." + i;
MyServiceProxy:代理创建类
![](https://image.cha138.com/20230329/590a443095da44cb81ae1eaec0b53def.jpg)
public class MyServiceProxy
public static IService getProxy(final IService iService)
/** 获取类加载器 */
ClassLoader classLoader = iService.getClass().getClassLoader();
/** 获取接口集合 */
Class<?>[] interfaces = iService.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
Object invoke = method.invoke(iService, args);
return invoke;
;
Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return (IService) proxyInstance;
Test:测试启动类
![](https://image.cha138.com/20230329/d38689f69858433e87893e8e5c9a6a12.jpg)
public class Test
public static void main(String[] args)
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
IService proxy = MyServiceProxy.getProxy(new MyService());
System.out.println(proxy.getClass());
String b = proxy.B(1);
System.out.println(b);
Debug启动
在下图箭头指向位置处打上断点,然后Debug启动
![](https://image.cha138.com/20230329/6b55f7b4ed434b838aadea176727a583.jpg)
点击step into,来到需要创建代理的接口类中
![](https://image.cha138.com/20230329/ac81856f44d6421fb32868ce8c4a021a.jpg)
点击step into,进入类MyServiceProxy
![](https://image.cha138.com/20230329/4d0fee787448486ba51c09c1d21c9505.jpg)
这个类中先获取类加载器,再获取接口们的集合,最后获取到handler后,3个参数一起传入newProxyInstance()方法中,用来创建我们的代理类
newProxyInstance()
我们进入newProxyInstance()方法中
![](https://image.cha138.com/20230329/6386579986dd403a8b769229d959b70c.jpg)
看下方法说明
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
简单翻一下:返回一个指定接口的代理类的实例,该代理类将方法调用分派给指定的调用处理程序。
一开始先判断传入的InvocationHandler是否为空,接着获取接口、获取安全管理器
![](https://image.cha138.com/20230329/24fbe7bbb1f24db5a0158c27aece6413.jpg)
然后是非常重要的一步:生成字节码文件
![](https://image.cha138.com/20230329/953b279b48354564a2e15db24641a86a.jpg)
getProxyClass0()
getProxyClass0()这个函数很复杂,我们进来瞅瞅(。・ω・。)ノ
![](https://image.cha138.com/20230329/54c1fe6c0ffd4c8f8beba858da0b6559.jpg)
第一步,先判断了下接口个数,对需要代理的对象实现的接口数量做了一个限制(不能超过65535个)
第二步,通过proxyClassCache的get()方法获取代理类并返回(通过缓冲区查询数值)
这里有两种情况:
如果实现给定接口的给定加载器定义的代理类存在,这将简单地返回缓存的副本
否则,它将通过ProxyClassFactory创建代理类
![](https://image.cha138.com/20230329/58d3636690c5463a92dcd24d84d1b324.jpg)
点击进入会执行到WeakCache中的get()方法(上图中可以看到proxyClassCache这个变量实际就是一个WeakCache对象)
get()
![](https://image.cha138.com/20230329/eafd5e9878f04cb68c25c229d7d7944e.jpg)
进入get()方法先判断传入的参数是否为空,然后移执行expungeStaleEntries()移除之前的内容
![](https://image.cha138.com/20230329/5177cad519e34688b3af49574ff65117.jpg)
尝试从map中获取key值,并经获取到的key值们转换成ConcurrentMap<Object, Supplier<V>>的类型
首先我们看下这个map是何方神圣
![](https://image.cha138.com/20230329/d749892eecce49f0ab1bd359bd4e2152.jpg)
这是一个map里面套着一个map,而且key的类型使用Object以便存储key值为null的对象
既然是尝试获取,就会出现两种情况:
获取到key
未获取到key
![](https://image.cha138.com/20230329/c4f4f9d7fec941dfb6645248588783f1.jpg)
如果未获取到会多执行一步putIfAbsent()方法(其余基本一致)
putIfAbsent()
![](https://image.cha138.com/20230329/734c766366f044299f86da43d7f4aad7.jpg)
在这个方法中,如果指定的键还没有与一个值相关联(或被映射为null),则将其与给定的值相关联并返回null,否则就会返回当前值(其实就是如果获取到的valuesMap为null,就把key为null,value为valuesMap的对象放入map中,如果oldValuesMap获取到值了,就会给valuesMap赋个值,但是目前并没有获取到相关联的值(如下图,执行完putIfAbsent()方法后,map的值增加了一个,但是valuesMap仍为空))
![](https://image.cha138.com/20230329/d2f2fd49bf1449478bd8b39f960b0bff.jpg)
点击step over继续执行下面的逻辑
![](https://image.cha138.com/20230329/499fa3059a4646428f67a40971d4a095.jpg)
出现了一个叫做"subKeyFactory"的变量,点击下进入到定义该变量的位置(就在这个类一开始)
![](https://image.cha138.com/20230329/ccd78005813a4ac29669ce09a15780c7.jpg)
这是一个BiFunction<K, P, ?>类型的变量,BiFunction这是一个函数式接口,代表叻一个接受两个参数并产生一个结果的函数(apply())
![](https://image.cha138.com/20230329/d044c794c3a7450ca9eeaac0d977f9d2.jpg)
subKeyFactory在WeakCache对象初始化的时候进行了赋值
![](https://image.cha138.com/20230329/244f1b4eb6fe45e9ae3b532f86547a5e.jpg)
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
所以subKeyFactory是有值的,调用叻他的apply()方法
![](https://image.cha138.com/20230329/5689053fd1e4472196a5af665416f15a.jpg)
由于我们的接口只有一个所以会返回return new Key1(interfaces[0])
![](https://image.cha138.com/20230329/824e802385f54d76a956981133c6d755.jpg)
此时已经获取到了接口对象,并从valuesMap中检索该子键所存储的可能的Supplier<V>,由于valuesMap是空,所以valuesMap.get(subKey)并未获取到值
接着创建一个Factory对象并赋值为null
![](https://image.cha138.com/20230329/0938462e54f144cbb0f44bcfe61643e7.jpg)
由于从valuesMap.get(subKey)并未获取到值,所以不会进入supplier != null的判断逻辑代码块,直接进入下面创建一个Factory对象
![](https://image.cha138.com/20230329/297bde5bbfd84e178a2c4051905e96b1.jpg)
这个Factory对象实现了Supplier<V>,实现了值的懒同步构建并将其放置到了缓存中
![](https://image.cha138.com/20230329/f6130073ba8b408c872405141da9ca7f.jpg)
把刚新建好的Factory赋值给变量supplier
![](https://image.cha138.com/20230329/06f209ba931d46f794270822f4a7afda.jpg)
由于while(true)是个死循环,因此会再次进入循环中,此时由于刚刚的赋值,变量supplier已经不是null叻,于是会调用supplier的get()方法
get()
![](https://image.cha138.com/20230329/fe358b900a094ed79cfb576d8d24679e.jpg)
![](https://image.cha138.com/20230329/430b70ed6d694459bbb5b896dd03f7f3.jpg)
apply()
在这个方法中,会生成我们代理对象的字节码文件
![](https://image.cha138.com/20230329/3bd9f2fb096e43939250176130cf81cc.jpg)
generateProxyClass()方法执行完成后,其实就生成了我们当前代理对象的字节码文件(apply()方法详解可以参考代理对象的字节码文件生成详解 )
![](https://image.cha138.com/20230329/98c22d441e774218a372c20d715573ba.jpg)
方法执行完成后,返回到get()方法
![](https://image.cha138.com/20230329/181d79de56524ea98efff965d6ec9371.jpg)
再返回到上一层的get()方法
![](https://image.cha138.com/20230329/f81eb93b34fa44368ad72f31d69bda25.jpg)
此时value也拿到叻值,接着通过return value跳出当前循环♻️
执行完getProxyClass0()方法,回到newProxyInstance()
![](https://image.cha138.com/20230329/4b76fbe7c1a24ccca65cb3760a1a5037.jpg)
此时我们已经生成了代理类的字节码文件,也获取到了代理类,并赋值给了变量cl
![](https://image.cha138.com/20230329/c76486d456f74973a158a82693094d61.jpg)
由于sm为null,跳过checkNewProxyPermission(),直接开始获取构造器,验证访问修饰符
![](https://image.cha138.com/20230329/401bf23fd07340b8a8b7b67e94dd1dcd.jpg)
通过调用获取到的构造器的newInstance()方法获取实例对象并返回
![](https://image.cha138.com/20230329/57b1743348b34bbba21f846435caa56f.jpg)
可以看到此时获取到的对象是com.aqin.custom.proxy.jdk.MyService@6d86b085
![](https://image.cha138.com/20230329/5c67630d5b4344e6ba143feeac4e210d.jpg)
返回到getProxy()
![](https://image.cha138.com/20230329/ebd18fe8e40c4d5d84a885f22577ffcd.jpg)
获取到了代理对象,继续把测试启动类执行完
![](https://image.cha138.com/20230329/2df94c42fdc347099d40a9648440e71b.jpg)
JDK介绍完啦,我们开始CGLIB
CGLIB
创建测试文件
MyService:被代理类
![](https://image.cha138.com/20230329/34ddf03cf3284a799e5446228ec0b61c.jpg)
public class MyServic
public void A()
System.out.println("This is A().");
public String B(int i)
return "This is B()." + i;
MyCglib:拦截器
![](https://image.cha138.com/20230329/f6ecbf8bebac46ab828260fbfd114f9e.jpg)
package com.aqin.custom.proxy.cglib;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Description
* @Author aqin1012 AQin.
* @Date 12/28/22 5:36 PM
* @Version 1.0
*/
public class MyCglib implements MethodInterceptor
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
Object o1 = methodProxy.invokeSuper(o, objects);
return o1;
需要注意的是:MethodInterceptor和MethodProxy都要选org.springframework.cglib.proxy包下的
Test:测试启动类
![](https://image.cha138.com/20230329/c588b96701bb4583869fa6142f581b4b.jpg)
package com.aqin.custom.proxy.cglib;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
/**
* @Description
* @Author aqin1012 AQin.
* @Date 12/28/22 5:29 PM
* @Version 1.0
*/
public class Test
public static void main(String[] args)
/** 动态代理创建的字节码文件存储到本地 */
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib_proxy");
/** 通过cg11b动态代理获取代理对象的过程,创建调用的对象 */
Enhancer enhancer = new Enhancer();
/** 设置enhancer对象的父类 */
enhancer.setSuperclass(MyService.class);
/** 设置enhancer的回调对象 */
enhancer.setCallback(new MyCglib());
/** 创建代理对象 */
MyService myService = (MyService) enhancer.create();
/** 通过代理对象调用目标方法 */
System.out.println(myService.B(999));
System.out.println(myService.getClass());
Debug启动
按照下图,在箭头指向的位置打上一个断点
![](https://image.cha138.com/20230329/3fd1cdb25c4a40a8ba5735c47af92130.jpg)
Debug的方式启动
![](https://image.cha138.com/20230329/164c0aad35894604a01f975889792cf9.jpg)
step into进入,会跳转到下图的位置
![](https://image.cha138.com/20230329/0d743a4f3c384f35b2752837e760ecb3.jpg)
继续向下执行,就会进入到Enhancer类中,这个类是生成动态子类以实现方法拦截的(JDK 1.3中引入),它并不需要被代理类一定实现了某个接口(这是与JDK动态代理的主要区别),动态生成的子类会覆盖超类的非最终方法,并回调到用户定义的拦截器实现的回调函数。
![](https://image.cha138.com/20230329/edad5c2d192049039b35d0ebfbf617b8.jpg)
继续执行到private static final EnhancerKey KEY_FACTORY = (EnhancerKey) KeyFactory.create(EnhancerKey.class, KeyFactory.HASH_ASM_TYPE, null);这行
![](https://image.cha138.com/20230329/e3942f71f94844ea83fed5a29671e4f7.jpg)
这句的作用是使用key工厂创建出对应class的代理类,后面的KeyFactory_HASH_ASM_TYPE即代理类中创建HashCode方法的策略
![](https://image.cha138.com/20230329/e0e48c3dff944f3d84c6003d1322d0f6.jpg)
而且在此处出现了两个对象:KeyFactory和EnhancerKey
![](https://image.cha138.com/20230329/3fab7765f2f6436496fbcfde214016af.jpg)
KeyFactory这个对象是生成处理多值键的类(多个键组合在一起的),用于Maps和Sets等集合中
![](https://image.cha138.com/20230329/35b96ab4b2ba44ce86f9ba49696a3261.jpg)
而Enhancer是一个内部接口,由于ClassLoader的问题,只能是public的
创建EnhancerKey
我们进入KeyFactory的create()方法中,开始EnhancerKey对象的创建
![](https://image.cha138.com/20230329/4615fa04c78f45ecafb9c8d2cd3a2b20.jpg)
再次进入return处的create()方法
![](https://image.cha138.com/20230329/1549d86adbbb4f29b8ffb7825030e3ca.jpg)
方法先创建了一个最简易的代理类生成器(只会生成HashCode()、equals()、toString()、newInstance()方法),然后设置接口类型,添加定制器,设置类加载器,最后调用gen.create()生成enhancerKey的代理类
于是我们再次进入create()方法
![](https://image.cha138.com/20230329/3591a278c05d4e509c6c72ea6f868cdb.jpg)
这个create()方法中的第一步设置了该生成器生成代理对象类的名字前缀;第二步调用了super的create()方法,终于!咱们的创建要开始叻( ̄∇ ̄)/
create()
![](https://image.cha138.com/20230329/5ddfff5e569b40b09982c191ab8065ad.jpg)
获取到当前生成器的类加载器loader,定义一个Map<ClassLoader,ClassLoaderData>类型的变量cache,并把CACHE赋值给cache
![](https://image.cha138.com/20230329/1f798f4196594a77aa0ddac7f38a94b7.jpg)
通过cache.get(loader)获取ClassLoaderData类型的结果data,不过此时cache里并没有值(CACHE是当前类在一开始初始化的一个Map<ClassLoader, ClassLoaderData>类型的空集合的变量,所以此时cache也为空)
![](https://image.cha138.com/20230329/2b374b42e87a4655abba3f71dcc0b1ea.jpg)
所以继续执行,会开始执行下图中条件代码块中的逻辑
![](https://image.cha138.com/20230329/7925423e422a498796b77c9cccedd79c.jpg)
即新建一个ClassLoaderData(loader)赋值给刚刚未获取到值的变量data
![](https://image.cha138.com/20230329/82e22f7cbefe445bb903245a2c6e8729.jpg)
此时点击step into,我们会来到ClassLoaderData类中的变量GET_KEY初始化的位置
![](https://image.cha138.com/20230329/4003afaad7d24027846e01ab0872161e.jpg)
这个GET_KEY是一个Function类型的变量
![](https://image.cha138.com/20230329/61575d5da0744f2b9ccd69258a7aac6a.jpg)
可以看出,Function类型是一个函数式接口,提供一个apply()方法,传入一个对象返回一个对象
public Object apply(AbstractClassGenerator gen)
return gen.key;
在此处的作用就是传入一个AbstractClassGenerator类型的变量gen,返回gen.key(key的定义位置见下图)
![](https://image.cha138.com/20230329/8de6eb79bcfe465ea690959452e50f67.jpg)
继续执行我们会进入ClassLoaderData的构造方法中,在这里为上面定义的变量赋值
new ClassLoaderData()
![](https://image.cha138.com/20230329/36fbe2a2d4f04c36a91402f48d7b7cf1.jpg)
第一步先判断类加载器不能为空,为空则抛出IllegalArgumentException的异常;接着设置类加载器(用的是弱引用类型WeakReference,即在下次垃圾回收时就会进行回收);
引用的强软弱虚(引用的强弱关系依次递减)
强引用(=):关系存在,即不会被垃圾回收机制回收(内存溢出抛异常也不会回收)
软引用(SoftReference):内存不够会被回收(回收后仍然OOM才会抛异常)
弱引用(WeakReference):无论内存够不够,垃圾收集器一工作就会被回收
虚引用(PhantomReference):约等于没有引用关系(甚至无法通过虚引用获取到对象实例),只是会在被回收后发送一条系统通知
然后新建了一个回调函数,这个回调函数的作用是当缓存中没获取到值时,会调用传入默认生成器AbstractclassGenerator的生成代理类并返回;最后,新建一个LoadingCache<AbstractClassGenerator, Object, Object>类型的缓存类,赋值给变量generatedClasses(这就是一个类型的变量,在类的一开始定义的)
![](https://image.cha138.com/20230329/0bd2186c0c094e089fc59bcf54822748.jpg)
而LoadingCache中有两个参数,一个是GET_KEY(就是刚刚解释的那个会返回gen.key的函数式接口),还有一个是load,而这个load也是一个function类型的函数式接口,返回的是一个gen.wrapCachedClass(klass)对象,其实也就是一个Class类型的对象
![](https://image.cha138.com/20230329/0e344f7dee0540cf9ffc0e906925e684.jpg)
即此缓存对象中包含的两个具体的function对象(其实就是具体的业务逻辑处理过程)
![](https://image.cha138.com/20230329/6b802d733ca046ba97a8f41b2011623d.jpg)
执行完构造函数返回,赋值给data,并放入缓存中
![](https://image.cha138.com/20230329/803b8aa2e06a466791156115d6026f9c.jpg)
此时我们的ClassLoaderData类型的变量data中包含了两个变量:一个key值和一个Class类型的对象,而这个对象被放入了CACHE中,因此此时CACHE中有了一个键值对
![](https://image.cha138.com/20230329/88b69037655e481b9aae2f1e05987eb5.jpg)
设置一个key值
![](https://image.cha138.com/20230329/d725bf0c49bd47c29bd715a13f54f820.jpg)
紧接着通过刚创建的data中调用get方法并将当前生成器以及是否使用缓存的标识(系统参数System.getProperty("cglib.useCache","true"))传入进去,返回的是生成好的代理类的class信息
get()
![](https://image.cha138.com/20230329/9667b89bb8884db4946544c8421bc10e.jpg)
进入get()方法中
![](https://image.cha138.com/20230329/bf1b18647fa848e2a06b68103a93f6f7.jpg)
如果不使用缓存,则直接调用生成器的命令
![](https://image.cha138.com/20230329/3880737d3b544a99bd22438032413b94.jpg)
不过一般都是默认使用缓存的(即getUserCache()返回的值为true),于是会将生成器作为参数传入到generatedClasses的get()方法中
![](https://image.cha138.com/20230329/7c925ec8ce84400ba4ff02474e53ff36.jpg)
可以看到此处的keyMapper就是在前面步骤中初始化好的ClassLoaderData
![](https://image.cha138.com/20230329/d1995a6527dc4c50bdc92338aa7a5b57.jpg)
返回的值就是gen.key,由于map为空,所以v值为null,于是进入到this.createEntry(key, cacheKey, v)方法中
![](https://image.cha138.com/20230329/cd9f247b49ac44f59445a9999ef1e97e.jpg)
由于v值为null,所以会进入else的代码块,新建一个FutureTask对象为task赋值,而FutureTask对象中是一个lambda表达式,调用一个call()方法,返回LoadingCache.this.loader.apply()
![](https://image.cha138.com/20230329/0a160a8c4b2946a0828f85cfe8a3f92a.jpg)
不知道大家对LoadingCache还有没有印象,之前初始化ClassLoaderData对象时,给变量generatedClasses赋的值就是一个LoadingCache对象,当时里面传入的两个参数,一个是GET_KEY(其实就是gen.key),一个是load,此处调用的LoadingCache.this.loader就是指的这个对象
![](https://image.cha138.com/20230329/95accd6953d547a7ac068ec7b3eda762.jpg)
因此调用LoadingCache.this.loader的apply()方法实际就是调用load这个lambda表达式中的apply()方法
![](https://image.cha138.com/20230329/f1646014d974428e9ba9e78a2663af8a.jpg)
所以在执行到task.run()时,会调用执行call()方法
![](https://image.cha138.com/20230329/35b17884be904c87a3b7062aa4043f6c.jpg)
点击step into进入run()方法,就会发现代码又执行到了刚才赋值的位置
![](https://image.cha138.com/20230329/ad4d9c7d801b4c56a2636ab80993d3bc.jpg)
再次点击step into,就会跳转到LoadingCache.this.loader.apply()方法中
![](https://image.cha138.com/20230329/6d58cf93b4db476589dfe4870a29b92a.jpg)
在此处调用执行的generate()方法,就是实际生成字节码文件的方法
![](https://image.cha138.com/20230329/93056d6d584846c2b234242070f55081.jpg)
先将当前的代理类生成器存入变量CURRENT中,CURRENT是一个ThreadLocal类型的变量(如下图)
![](https://image.cha138.com/20230329/fe29113b70c04b0c83d7a4247701a2b6.jpg)
然后从传入的ClassLoaderData中获取类加载器classLoader,判断其是否为空,为空则抛出IllegalStateException异常;不为空则继续执行后面的逻辑
![](https://image.cha138.com/20230329/964e90354e1a4157bdb54b213722bb6b.jpg)
在获取到类加载器后,我们还需要获取到代理类的名字,于是generateClassName()方法开始生成代理类的名字
![](https://image.cha138.com/20230329/513efb3e98cf449394f4283ece1479db.jpg)
getClassName()方法中是具体的生成规则(这点要比JDK生成代理对象名称要复杂,JDK就是获取包名,然后加“$Proxy”+0、1、2、3……)
![](https://image.cha138.com/20230329/a56dc4e3d2d947189a9bbfb716ad2ea2.jpg)
可以看下这次最终生成的代理类的名字
![](https://image.cha138.com/20230329/a3fb6f411af442deb9e3c753c8d7bfac.jpg)
有了名字后,回到generate()方法中将它缓存进传入的变量data中,有了类加载器,有了名字,接下来就开始生成字节码了
![](https://image.cha138.com/20230329/890f5e5367bf43d782ce8156863ee132.jpg)
![](https://image.cha138.com/20230329/3f55729fc6d84b9cac8c2f553444eaf0.jpg)
进入generateClass()方法中,在这里为字节码文件写入方法
![](https://image.cha138.com/20230329/e6490033f1724c46836b81abd95506f1.jpg)
先创建了一个ClassEmitter对象,尝试获取被代理类的newInstance()方法,如果没有会报异常(由此可知,如果想用Generator代理类生成器,newInstance()方法必不可少)此处我们代理的Enchaer、EnhancerKey、newInstance方法返回值为Object
接着找到newInstance()方法的所有参数类型放入集合parameterTypes中当做成员变量
![](https://image.cha138.com/20230329/cb12ad6f4ba74cf3bc82e50ac36deef1.jpg)
接下来开始具体的写入操作
先通过begin_class()创建类开始写入类头,版本号,访问权限,类名等通用信息;接着调用EmitUtils.null_constructor()写入无参构造方法;调用EmitUtils.factory_method()写入newInstance()方法
![](https://image.cha138.com/20230329/9a5d7d9f70604c8eb6238ca25a730463.jpg)
随后调用ce.begin_method()开始构造有参构造方法
![](https://image.cha138.com/20230329/70cd23b701e04b4e868039b2106dbcd3.jpg)
有参构造中调用父类构造方法(即super.构造方法())
找到传入的定制器(例如一开始传入的hashCode()方法定制器)
遍历成员变量(即newInstance()方法的所有参数)
将这些参数全部声明写入到类中
设置每个成员变量的值(this.xxx=xxx)
![](https://image.cha138.com/20230329/d09fc8a0e35241e1962eb0457606bea3.jpg)
设置返回值,至此完成有参构造及成员变量的写入
最后还有一些Object的固定方法需要写入(包括hashcode()、equals()、toString())
![](https://image.cha138.com/20230329/f7a20a4e414a46a0b3bd4e2b146c127f.jpg)
最后执行到ce.end_class(),类写入结束,至此类信息收集完成并全部写入ClassEmitter类型的变量ce中
![](https://image.cha138.com/20230329/799b8723782b474289b28f9ce3aa2ed1.jpg)
方法结束,返回字节码
![](https://image.cha138.com/20230329/7c78775e43294688a9395fb8986711fb.jpg)
通过类加载器加载到内存,返回到apply()方法中
![](https://image.cha138.com/20230329/76f5a94725e54602887458e6085c369b.jpg)
获取generatedClasses的get()方法的返回值后,解包装并返回
![](https://image.cha138.com/20230329/de00856135114e41bfa284b2475ede21.jpg)
此时的cachedValue就有值了,返回给我们的obj
![](https://image.cha138.com/20230329/e49b6b02fc104b94b8a297bdd5d6c419.jpg)
在get()方法执行结束后,会判断获取到Object对象是否为Class的实例对象,由于Class类型的对象我们是没有办法直接使用的,所以需要通过调用firstInstance()方法进行实例化
![](https://image.cha138.com/20230329/28630801c76344768af3fb3ac0b65d86.jpg)
![](https://image.cha138.com/20230329/88358ad63d824ae2a44376d6963b0e4e.jpg)
如果为Class则实例化并返回我们需要的代理类,如果不是则说明是实体,则直接执行另一个方法返回实体。create()执行结束后一路返回
![](https://image.cha138.com/20230329/b21794cbb1a84d47b0ed42f4ce9dc961.jpg)
继续执行完后面的静态代码块就会回到我们的测试启动类,紧接着对superclass和callback两个属性进行赋值
![](https://image.cha138.com/20230329/af89a0b19c3e495d993e957b5c5f8c94.jpg)
spring aop原理 JDK动态代理和CGLIB动态代理
Spring的两大特性是IOC和AOP
IOC负责将对象动态的注入到容器,从而达到一种需要谁就注入谁,什么时候需要就什么时候注入的效果。理解spring的ioc也很重要。
但是今天主要来和大家讲讲aop。
AOP 广泛应用于处理一些具有横切性质的系统级服务,AOP 的出现是对 OOP 的良好补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。
AOP实现的关键在于AOP框架自动创建的AOP代理。
AOP代理主要分为静态代理和动态代理,
静态代理的代表为AspectJ;
动态代理则以Spring AOP为代表
1,AspectJ
AspectJ是静态代理的增强,采用编译时生成 AOP 代理类,因此也称为编译时增强,具有更好的性能。
缺点:但需要使用特定的编译器进行处理
2,Spring AOP
Spring AOP使用的动态代理,运行时生成 AOP 代理类,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
缺点:由于 Spring AOP 需要在每次运行时生成 AOP 代理,因此性能略差一些。
由于aspectj的使用还需要使用特定的编译器进行处理,处理起来有点麻烦。今天重要来讲解Spring AOP
Spring AOP动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。
JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类(通过修改字节码来实现代理)。
注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
jdk和cglib动态代理来共同实现我们的aop面向切面的功能。
下面就来用简单的代码来演示下jdk和cglib动态代理的实现原理。
一,jdk动态代理实现AOP拦截
1,为target目标类定义一个接口JdkInterface,这是jdk动态代理实现的前提
/**
* Created by qcl on 2018/11/29
* desc: jdk动态aop代理需要实现的接口
*/
public interface JdkInterface {
public void add();
}
2,用我们要代理的目标类JdkClass实现上面我们定义的接口,我们的实验目标就是在不改变JdkClass目标类的前提下,在目标类的add方法的前后实现拦截,加入自定义切面逻辑。这就是aop的魅力所在:代码与代码之间没有耦合。
/**
* Created by qcl on 2018/11/29
* desc: 被代理的类,即目标类target
*/
public class JdkClass implements JdkInterface {
@Override
public void add() {
System.out.println("目标类的add方法");
}
}
3 ,到了关键的一步,用我们的MyInvocationHandler,实现InvocationHandler接口,并且实现接口中的invoke方法。仔细看invoke方法,就是在该方法中加入切面逻辑的。目标类方法的执行是由mehod.invoke(target,args)这条语句完成。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Created by qcl on 2018/11/29
* desc:这里加入切面逻辑
*/
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before-------切面加入逻辑");
Object invoke = method.invoke(target, args);//通过反射执行,目标类的方法
System.out.println("after-------切面加入逻辑");
return invoke;
}
}
4,测试结果
/**
* Created by qcl on 2018/11/29
* desc:测试
*/
public class JdkTest {
public static void main(String[] args) {
JdkClass jdkClass = new JdkClass();
MyInvocationHandler handler = new MyInvocationHandler(jdkClass);
// Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
//这里的proxyInstance就是我们目标类的增强代理类
JdkInterface proxyInstance = (JdkInterface) Proxy.newProxyInstance(jdkClass.getClass().getClassLoader(),
jdkClass.getClass()
.getInterfaces(), handler);
proxyInstance.add();
//打印增强过的类类型
System.out.println("=============" + proxyInstance.getClass());
}
}
执行上面测试类可以得到如下结果
可以看到,目标类的add方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。再看class com.sun.proxy.$Proxy0。这里进一步证明JDK动态代理的核心是InvocationHandler接口和Proxy类
二,cglib动态代理实现AOP拦截
1,定义一个要被代理的Base目标类(cglib不需要定义接口)
/**
* Created by qcl on 2018/11/29
* desc:要被代理的类
*/
public class Base {
public void add(){
System.out.println("目标类的add方法");
}
}
2,定义CglibProxy类,实现MethodInterceptor接口,实现intercept方法。该代理的目的也是在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args)
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by qcl on 2018/11/29
* desc:这里加入切面逻辑
*/
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("before-------切面加入逻辑");
methodProxy.invokeSuper(object, args);
System.out.println("after-------切面加入逻辑");
return null;
}
}
3,测试类
/**
* Created by qcl on 2018/11/29
* desc:测试类
*/
public class CglibTest {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Base.class);
//回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
enhancer.setCallback(proxy);
//此刻,base不是单车的目标类,而是增强过的目标类
Base base = (Base) enhancer.create();
base.add();
Class<? extends Base> baseClass = base.getClass();
//查看增强过的类的父类是不是未增强的Base类
System.out.println("增强过的类的父类:"+baseClass.getSuperclass().getName());
System.out.println("============打印增强过的类的所有方法==============");
FanSheUtils.printMethods(baseClass);
//没有被增强过的base类
Base base2 = new Base();
System.out.println("未增强过的类的父类:"+base2.getClass().getSuperclass().getName());
System.out.println("=============打印增未强过的目标类的方法===============");
FanSheUtils.printMethods(base2.getClass());//打印没有增强过的类的所有方法
}
}
下面是打印结果
通过打印结果可以看到
cglib动态的拦截切入成功了
cglib动态代理的方式是在运行时动态的生成目标类(Base)的子类,并且在目标类现有方法的基础上添加了很多cglib特有的方法。 下面贴出用反射打印类所有方法的工具类
public class FanSheUtils {
//打印该类的所有方法
public static void printMethods(Class cl) {
System.out.println();
//获得包含该类所有其他方法的数组
Method[] methods = cl.getDeclaredMethods();
//遍历数组
for (Method method : methods) {
System.out.print(" ");
//获得该方法的修饰符并打印
String modifiers = Modifier.toString(method.getModifiers());
if (modifiers.length() > 0) {
System.out.print(modifiers + " ");
}
//打印方法名
System.out.print(method.getName() + "(");
//获得该方法包含所有参数类型的Class对象的数组
Class[] paramTypes = method.getParameterTypes();
//遍历数组
for (int i = 0; i < paramTypes.length; i++) {
if (i > 0) {
System.out.print(",");
}
System.out.print(paramTypes[i].getName());
}
System.out.println(");");
}
}
}
注意:上面用到了cglib.jar和asm.jar。在我们的maven的pom.xml里引入下面类库即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
以上是关于保姆级手把手捋动态代理流程(JDK+Cglib超详细源码分析)的主要内容,如果未能解决你的问题,请参考以下文章