CGLIB 动态代理

参考链接:https://blog.csdn.net/yhl_jxy/article/details/80633194
参考链接:https://www.jianshu.com/p/001f866a49d7

1.CGLIB 简单介绍

CGLIB简介:CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用。

2.Demo

2.1 创建需要被代理的类
public class Student2 {public String getName() {System.out.println("我叫红领巾");return "我叫红领巾";}public Integer getAge() {System.out.println("14");return 14;}
}
2.2 生成代理类 需实现MethodInterceptor接口
public class MyProxy implements MethodInterceptor {/*** Enhancer.create(superClass,callback)* superClass: 生成代理对象的父类* callback:设置enhancer的回调对象**/public <T> T getProxy(Class<T> clazz){return (T) Enhancer.create(clazz,this);}/*** target:cglib生成的代理对象* method:被代理对象方法* args:方法入参* methodProxy: 代理方法**/@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("前置");//用MethodProxy 调用父类方法。Object returnObject =  methodProxy.invokeSuper(target,args);System.out.println("后置");return returnObject;}
}
2.3 生成测试类
public class ProxyTest {public static void main(String[] args) {// 代理类class文件存入本地磁盘方便我们反编译查看源码System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/admin");MyProxy myProxy = new MyProxy();Student2 student2 =  myProxy.getProxy(Student2.class);student2.getName();}
}
2.4 运行结果
2.5 生成的代理类class文件

3. 源码解析

在Demo中通过代理类 Enhancer.create(clazz,this)方法返回被代理类

public static Object create(Class type, Callback callback) {Enhancer e = new Enhancer();e.setSuperclass(type);e.setCallback(callback);return e.create();}

该方法就是创建一个Enhancer类通过create方法返回被代理类

public Object create() {classOnly = false;argumentTypes = null;return createHelper();}

该方法含义就是如果有必要就创建一个新类,并且用指定的回调对象创建一个新的对象实例,

使用的父类的参数的构造方法来实例化父类的部分。核心内容在createHelper()中,源码如下:

private Object createHelper() {this.preValidate();Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces),this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction,this.serialVersionUID);this.currentKey = key;Object result = super.create(key);return result;}

preValidate()方法校验callbackTypes、filter是否为空,以及为空时的处理。
通过newInstance()方法创建EnhancerKey对象,作为Enhancer父类AbstractClassGenerator.create()方法

protected Object create(Object key) {try {ClassLoader loader = this.getClassLoader();// 如果 data 不存在 ,在多线程环境下添加到CACHEMap<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHEAbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);if (data == null) {Class var5 = AbstractClassGenerator.class;synchronized(AbstractClassGenerator.class) {cache = CACHE;data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);if (data == null) {Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);data = new AbstractClassGenerator.ClassLoaderData(loader);newCache.put(loader, data);CACHE = newCache;}}}this.key = key;Object obj = data.get(this, this.getUseCache());return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);} catch (Error | RuntimeException var9) {throw var9;} catch (Exception var10) {throw new CodeGenerationException(var10);}}

真正创建代理对象方法在nextInstance()方法中,该方法为抽象类AbstractClassGenerator的一个方法,签名如下:
abstract protected Object nextInstance(Object instance) throws Exception;

在子类Enhancer中实现,实现源码如下:


protected Object nextInstance(Object instance) {EnhancerFactoryData data = (EnhancerFactoryData) instance;if (classOnly) {return data.generatedClass;}Class[] argumentTypes = this.argumentTypes;Object[] arguments = this.arguments;if (argumentTypes == null) {argumentTypes = Constants.EMPTY_CLASS_ARRAY;arguments = null;}return data.newInstance(argumentTypes, arguments, callbacks);}

通过data.newInstance(argumentTypes, arguments, callbacks)方法,
第一个参数为代理对象的构成器类型,第二个为代理对象构造方法参数,第三个为对应回调对象。
最后根据这些参数,通过反射生成代理对象,源码如下:

/*** Creates proxy instance for given argument types, and assigns the callbacks.* Ideally, for each proxy class, just one set of argument types should be used,* otherwise it would have to spend time on constructor lookup.* Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},* with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"** @see #createUsingReflection(Class)* @param argumentTypes constructor argument types* @param arguments constructor arguments* @param callbacks callbacks to set for the new instance* @return newly created proxy*/public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {setThreadCallbacks(callbacks);try {// Explicit reference equality is added here just in case Arrays.equals does not have oneif (primaryConstructorArgTypes == argumentTypes ||Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {// If we have relevant Constructor instance at hand, just call it// This skips "get constructors" machineryreturn ReflectUtils.newInstance(primaryConstructor, arguments);}// Take a slow path if observing unexpected argument typesreturn ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);} finally {// clear thread callbacks to allow them to be gc'dsetThreadCallbacks(null);}}

生成的代理对象的class文件进行反编译之后的代码如下

public class Student2$$EnhancerByCGLIB$$3859af2f extends Student2 implements Factory {private boolean CGLIB$BOUND;public static Object CGLIB$FACTORY_DATA;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback[] CGLIB$STATIC_CALLBACKS;private MethodInterceptor CGLIB$CALLBACK_0;private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$getName$0$Method;private static final MethodProxy CGLIB$getName$0$Proxy;private static final Object[] CGLIB$emptyArgs;private static final Method CGLIB$getAge$1$Method;private static final MethodProxy CGLIB$getAge$1$Proxy;private static final Method CGLIB$equals$2$Method;private static final MethodProxy CGLIB$equals$2$Proxy;private static final Method CGLIB$toString$3$Method;private static final MethodProxy CGLIB$toString$3$Proxy;private static final Method CGLIB$hashCode$4$Method;private static final MethodProxy CGLIB$hashCode$4$Proxy;private static final Method CGLIB$clone$5$Method;private static final MethodProxy CGLIB$clone$5$Proxy;static void CGLIB$STATICHOOK1() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class clazz1 = Class.forName("com.domain.Student2$$EnhancerByCGLIB$$3859af2f");Class clazz2;CGLIB$equals$2$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[0];CGLIB$equals$2$Proxy = MethodProxy.create(clazz2, clazz1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");CGLIB$toString$3$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[1];CGLIB$toString$3$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");CGLIB$hashCode$4$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[2];CGLIB$hashCode$4$Proxy = MethodProxy.create(clazz2, clazz1, "()I", "hashCode", "CGLIB$hashCode$4");CGLIB$clone$5$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[3];CGLIB$clone$5$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods());CGLIB$getName$0$Method = ReflectUtils.findMethods(new String[] { "getName", "()Ljava/lang/String;", "getAge", "()Ljava/lang/Integer;" }, (clazz2 = Class.forName("com.domain.Student2")).getDeclaredMethods())[0];CGLIB$getName$0$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/String;", "getName", "CGLIB$getName$0");CGLIB$getAge$1$Method = ReflectUtils.findMethods(new String[] { "getName", "()Ljava/lang/String;", "getAge", "()Ljava/lang/Integer;" }, (clazz2 = Class.forName("com.domain.Student2")).getDeclaredMethods())[1];CGLIB$getAge$1$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/Integer;", "getAge", "CGLIB$getAge$1");ReflectUtils.findMethods(new String[] { "getName", "()Ljava/lang/String;", "getAge", "()Ljava/lang/Integer;" }, (clazz2 = Class.forName("com.domain.Student2")).getDeclaredMethods());}final String CGLIB$getName$0() { return super.getName(); }public final String getName() {if (this.CGLIB$CALLBACK_0 == null) {this.CGLIB$CALLBACK_0;CGLIB$BIND_CALLBACKS(this);} return (this.CGLIB$CALLBACK_0 != null) ? (String)this.CGLIB$CALLBACK_0.intercept(this, CGLIB$getName$0$Method, CGLIB$emptyArgs, CGLIB$getName$0$Proxy) : super.getName();}......................................
}

从上述代码可以看到 如果被代理类调用getName()方法,就会执行 代理类的intercept()方法


//  CGLIB$getName$0$Method = ReflectUtils.findMethods(new String[] { "getName", "()Ljava/lang/String;", "getAge", "()Ljava/lang/Integer;" }, (clazz2 = Class.forName("com.domain.Student2")).getDeclaredMethods())[0];
//  CGLIB$getName$0$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/String;", "getName", "CGLIB$getName$0");clazz2 = Class.forName("com.domain.Student2"))
clazz1 =  Class.forName("com.domain.Student2$$EnhancerByCGLIB$$3859af2f");//下面看下MethodProxy.create()方法
c1:  Student
c2: Student$$Enhancer
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {MethodProxy proxy = new MethodProxy();proxy.sig1 = new Signature(name1, desc);proxy.sig2 = new Signature(name2, desc);proxy.createInfo = new CreateInfo(c1, c2);return proxy;}this.CGLIB$CALLBACK_0.intercept(this, CGLIB$getName$0$Method, CGLIB$emptyArgs, CGLIB$getName$0$Proxy)

在MethodProxy中

对象 实例
c1 Student2
f1 c1的fastClass,更方便调用c1的函数
c2 Student2$$Enhancer,生成的代理对象的实例
f2 c2的fastClass,更加方便调用c2的函数

在intercept()方法中,调用MethodProxy的invokeSuper()方法

 public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("前置");Object returnObject =  methodProxy.invokeSuper(target,args);System.out.println("后置");return returnObject;}

invokeSuper()

   public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {this.init();MethodProxy.FastClassInfo fci = this.fastClassInfo;//通过下面的代码分析  可知此处调用是 Student$$Enhancer$$FastClss的invoke方法return fci.f2.invoke(fci.i2, obj, args);} catch (InvocationTargetException var4) {throw var4.getTargetException();}}
//下面是init() 方法
private void init() {if (this.fastClassInfo == null) {synchronized(this.initLock) {if (this.fastClassInfo == null) {MethodProxy.CreateInfo ci = this.createInfo;MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();// f1对应的是 Student的FastClassfci.f1 = helper(ci, ci.c1);// f2对应的是 Student$$Enhancer 的FastClassfci.f2 = helper(ci, ci.c2);//i1i2分别是分别在Student$$FastClass和Student$$Enhancer$$FastClss中 调用方法的index.fci.i1 = fci.f1.getIndex(this.sig1);fci.i2 = fci.f2.getIndex(this.sig2);this.fastClassInfo = fci;this.createInfo = null;}}}}//init() 中又调用helper()方法//根据ci得到该方法的委托类,实现类,分别生成这两个类的fastClassprivate static FastClass helper(MethodProxy.CreateInfo ci, Class type) {Generator g = new Generator();g.setType(type);g.setContextClass(type);g.setClassLoader(ci.c2.getClassLoader());g.setNamingPolicy(ci.namingPolicy);g.setStrategy(ci.strategy);g.setAttemptLoad(ci.attemptLoad);return g.create();}

通过反编译得到Student$$EnchancerFastClass的invoke()方法

public Object invoke(int paramInt, Object paramObject, Object[] paramArrayOfObject) throws InvocationTargetException {try {switch (paramInt) {case 0:return new Boolean(((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).equals(paramArrayOfObject[0]));case 1:return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).toString();..........//找到Student2$$Enhancer 的getName$0()方法。case 17:return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$getName$0();case 18:return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$getAge$1();case 19:return new Boolean(((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$equals$2(paramArrayOfObject[0]));case 20:return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$toString$3();case 21:return new Integer(((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$hashCode$4());case 22:return ((Student2$$EnhancerByCGLIB$$3859af2f)paramObject).CGLIB$clone$5();} } catch (Throwable throwable) {throw new InvocationTargetException(null);} throw new IllegalArgumentException("Cannot find matching method/constructor");}

在Student$Enhancer中调用CGLIBEnhancer中调用CGLIBEnhancer中调用CGLIBgetName$0()调用 super. name()方法 即 Student 的getName方法();

final String CGLIB$getName$0() { return super.getName(); }

思考

思考
1.fastclass比反射快的原因
通过方法前面或者标识符index,利用switch case直接利用对象去调用函数
而反射是java.lang.reflect.Method#invoke,稍微复杂点,这个没研究过具体实现

2.MethodProxy#invoke和MethodProxy#invokeSuper什么区别,即[c1,f1]与[c2,f2]的区别
[c1,f1]对应的是 父类的class和fastclass
[c2,f2]对应的是 父类的enhanceClass和 enhanceFastClass

3.MethodProxy#init创建fastclass时,每个method在第一次调用时,都会进行
net.sf.cglib.proxy.MethodProxy#init
net.sf.cglib.proxy.MethodProxy#helper
net.sf.cglib.reflect.FastClass.Generator#create
那么为什么对应的fastclass文件只生成了一次(不是一个method调用一次就生成一次)
并且一次就有整个类的信息,而不是只有这个method相关信息呢

第一点:同一个类的fastClass只生成了一次,
net.sf.cglib.reflect.FastClass.Generator#create
net.sf.cglib.core.AbstractClassGenerator#create
里面用了缓存
第二点:一次就有整个类的信息,而不是只有这个method信息
net.sf.cglib.proxy.MethodProxy#create时就传入和class c1,c2
后来创建fastClass时
net.sf.cglib.proxy.MethodProxy#helper
调用了g.setType(type);
在fastClass生成时
net.sf.cglib.reflect.FastClass.Generator#generateClass
用到了这个之前设置好的Class type,也就直到类信息了
invokeSuper的逻辑

4.把invokeSuper改成invoke会怎么样
结论:死循环,堆栈溢出

访问StudentEnhancer中getName()方法然后进入MethodInterceptor的interceptor()方法又去调用StudentEnhancer中 getName()方法 然后进入MethodInterceptor的interceptor()方法 又去调用StudentEnhancer中getName()方法然后进入MethodInterceptor的interceptor()方法又去调用StudentEnhancer中 getName()方法 …
导致死循环

CGLIB 动态代理用例及源码解析相关推荐

  1. JDK动态代理用例及源码解析

    动态代理分为JDK 动态代理和CGLIB 动态代理 参考链接:https://www.jianshu.com/p/269afd0a52e6 一. JDK 动态代理 实现JDK动态代理必须实现接口. 实 ...

  2. 动态代理及JDK代理源码解析

    动态代理及JDK代理源码解析 参考:JDK动态代理-超详细源码分析 - 简书 (jianshu.com) 文章目录 动态代理及JDK代理源码解析 一.为什么需要动态代理 什么是代理模式? 静态代理: ...

  3. 【开源项目】动态线程池框架Hippo4j源码解析

    动态线程池框架Hippo4j源码解析 项目简介 Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力. 快速开始 https://hippo4 ...

  4. 浅谈Spring中JDK动态代理与CGLIB动态代理

    前言 Spring是Java程序员基本不可能绕开的一个框架,它的核心思想是IOC(控制反转)和AOP(面向切面编程).在Spring中这两个核心思想都是基于设计模式实现的,IOC思想的实现基于工厂模式 ...

  5. Dubbo 实现原理与源码解析系列 —— 精品合集

    摘要: 原创出处 http://www.iocoder.cn/Dubbo/good-collection/ 「芋道源码」欢迎转载,保留摘要,谢谢! 1.[芋艿]精尽 Dubbo 原理与源码专栏 2.[ ...

  6. QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数

    版权声明 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者"tingsking18"和主站点地址,方便其他朋友提问和指正. QT源码解析(一) QT创建窗口程序.消 ...

  7. Redis源码解析(15) 哨兵机制[2] 信息同步与TILT模式

    Redis源码解析(1) 动态字符串与链表 Redis源码解析(2) 字典与迭代器 Redis源码解析(3) 跳跃表 Redis源码解析(4) 整数集合 Redis源码解析(5) 压缩列表 Redis ...

  8. Spring源码学习(四) | @Configuration的cglib动态代理

    文章目录 前言 例子 @Configuration :full or lite 设置 full or lite Cglib生成代理类AppConfig Where is it generated Ho ...

  9. Spring AOP源码解析——AOP动态代理原理和实现方式

    2019独角兽企业重金招聘Python工程师标准>>> Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和 ...

最新文章

  1. 如何使用Coded UI Test对Webpage进行自动化测试
  2. python 练习题-去重排序与字符串分割
  3. python定时器 是线程吗_python线程定时器Timer(32)
  4. JavaScript indexOf() 方法
  5. android 测试人员测试时使用release版本还是debug版本_为什么做软件测试
  6. 机器学习理论基础 集成学习前期基础--分类决策树与回归决策树
  7. AI 又进阶!除了鉴别 PS 图片,还能一键卸妆!
  8. java链表实现二叉树_Java 实现链表、二叉树的一些问题
  9. 拖拽 开发 easyui php,Easyui-Builder
  10. android启动过程之init.rc文件浅析
  11. 安装 Redis的Python客户端redis-py
  12. 110 同步、异步、阻塞、非阻塞
  13. 推荐一款UI非常Good的 Redis 客户端工具
  14. winpe进入linux系统,制作U盘Linux 与WinPE启动
  15. java 锟斤 解决乱码_java eclipse 开发中文乱码锟斤拷小锟斤拷锟
  16. Java中限定类名和非限定类名的区别
  17. linux setlocale函数,linux中的多语言环境(LC_ALL, LANG, locale)
  18. 食品如何寄国际快递到美国
  19. android+如何设置单屏壁纸,给你一个设置单屏壁纸的软件
  20. iptables、firewalld防火墙详解

热门文章

  1. 基于单片机的智能浇花(灌溉)系统设计
  2. Deep Learning(深度学习)学习笔记整理系列之常用模型
  3. 【ESP 保姆级教程】疯狂传感器篇 —— 案例:Mega + ESP8266 + MQ2烟雾 + MQ3酒精 + MQ7一氧化碳+ OLED + 巴法云 + 微信小程序(环境监控)
  4. java 建行接口_中国建设银行网上支付接口以及自动对账
  5. 4399曹政:中国互联网
  6. 从零基础如何自学 UI 设计?
  7. onekeyghost 备份系统
  8. k8s RoCE 部署: k8s-rdma-shared-dev-plugin + macvlan cni
  9. python常用函数详解(持续更新ing)
  10. CentOS6开关机日志查询