Cglib动态代理实现方式

我们先通过一个demo看一下Cglib是如何实现动态代理的。

首先定义个服务类,有两个方法并且其中一个方法用final来修饰。

public class PersonService {public PersonService() {System.out.println("PersonService构造");}
    //该方法不能被子类覆盖final public Person getPerson(String code) {System.out.println("PersonService:getPerson>>"+code);return null;}public void setPerson() {System.out.println("PersonService:setPerson");}
}

Cglib是无法代理final修饰的方法的,具体原因我们一会通过源码来分析。

然后,定义一个自定义MethodInterceptor。

public class CglibProxyIntercepter implements MethodInterceptor {@Overridepublic Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("执行前...");Object object = methodProxy.invokeSuper(sub, objects);System.out.println("执行后...");return object;}
}

我们看一下intercept方法入参,sub:cglib生成的代理对象,method:被代理对象方法,objects:方法入参,methodProxy:代理方法

最后,我们写个例子调用一下,并将Cglib生成的代理类class文件输出磁盘方便我们反编译查看源码。

public class Test {public static void main(String[] args) {//代理类class文件存入本地磁盘System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");Enhancer enhancer = new Enhancer();enhancer.setSuperclass(PersonService.class);enhancer.setCallback(new CglibProxyIntercepter());PersonService proxy= (PersonService)  enhancer.create();proxy.setPerson();        proxy.getPerson("1");     } }

我们执行一下会发现getPerson因为加final修饰并没有被代理,下面我们通过源码分析一下。

执行前...
PersonService:setPerson
执行后...
PersonService:getPerson>>1

生成代理类

执行Test测试类可以得到Cglib生成的class文件,一共有三个class文件我们反编译以后逐个说一下他们的作用。

PersonService$$EnhancerByCGLIB$$eaaaed75就是cglib生成的代理类,它继承了PersonService类。
public class PersonService$$EnhancerByCGLIB$$eaaaed75extends PersonServiceimplements Factory
{private boolean CGLIB$BOUND;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback[] CGLIB$STATIC_CALLBACKS;private MethodInterceptor CGLIB$CALLBACK_0;//拦截器private static final Method CGLIB$setPerson$0$Method;//被代理方法private static final MethodProxy CGLIB$setPerson$0$Proxy;//代理方法private static final Object[] CGLIB$emptyArgs;private static final Method CGLIB$finalize$1$Method;private static final MethodProxy CGLIB$finalize$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 localClass1 = Class.forName("com.demo.proxy.cglib.PersonService$$EnhancerByCGLIB$$eaaaed75");//代理类
    Class localClass2;//被代理类PersionServiceMethod[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());CGLIB$finalize$1$Method = tmp95_92[0];CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");Method[] tmp115_95 = tmp95_92;CGLIB$equals$2$Method = tmp115_95[1];CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");Method[] tmp135_115 = tmp115_95;CGLIB$toString$3$Method = tmp135_115[2];CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");Method[] tmp155_135 = tmp135_115;CGLIB$hashCode$4$Method = tmp155_135[3];CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");Method[] tmp175_155 = tmp155_135;CGLIB$clone$5$Method = tmp175_155[4];CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");tmp175_155; Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "setPerson", "()V" }, (localClass2 = Class.forName("com.demo.proxy.cglib.PersonService")).getDeclaredMethods());CGLIB$setPerson$0$Method = tmp223_220[0];CGLIB$setPerson$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "setPerson", "CGLIB$setPerson$0");tmp223_220;return;}

我们通过代理类的源码可以看到,代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应,比如 Method CGLIB$setPerson$0$Method、MethodProxy CGLIB$setPerson$0$Proxy;

方法的调用

//代理方法(methodProxy.invokeSuper会调用)   final void CGLIB$setPerson$0() {super.setPerson();}//被代理方法(methodProxy.invoke会调用,这就是为什么在拦截器中调用methodProxy.invoke会死循环,一直在调用拦截器)public final void setPerson() {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(this.CGLIB$CALLBACK_0 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if(var10000 != null) { //调用拦截器
         var10000.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy);
      } else {super.setPerson();}}

调用过程:代理对象调用this.setPerson方法->调用拦截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理对象setPerson方法

MethodProxy

拦截器MethodInterceptor中就是由MethodProxy的invokeSuper方法调用代理方法的,MethodProxy非常关键,我们分析一下它具体做了什么。

  • 创建MethodProxy
public class MethodProxy {private Signature sig1;private Signature sig2;private MethodProxy.CreateInfo createInfo;private final Object initLock = new Object();private volatile MethodProxy.FastClassInfo fastClassInfo;  //c1:被代理对象Class    //c2:代理对象Class    //desc:入参类型    //name1:被代理方法名    //name2:代理方法名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 MethodProxy.CreateInfo(c1, c2);return proxy;}
private static class CreateInfo {    Class c1;    Class c2;    NamingPolicy namingPolicy;    GeneratorStrategy strategy;    boolean attemptLoad;

    public CreateInfo(Class c1, Class c2) {        this.c1 = c1;        this.c2 = c2;        AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();        if(fromEnhancer != null) {            this.namingPolicy = fromEnhancer.getNamingPolicy();            this.strategy = fromEnhancer.getStrategy();            this.attemptLoad = fromEnhancer.getAttemptLoad();        }

    }}
 

  • invokeSuper调用
public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {this.init();MethodProxy.FastClassInfo fci = this.fastClassInfo;return fci.f2.invoke(fci.i2, obj, args);} catch (InvocationTargetException var4) {throw var4.getTargetException();}}
private static class FastClassInfo {    FastClass f1;//被代理类FastClass    FastClass f2;//代理类FastClass    int i1; //被代理类的方法签名(index)    int i2;//代理类的方法签名

    private FastClassInfo() {    }}
 

上面代码调用过程就是获取到代理类对应的FastClass,并执行了代理方法。还记得之前生成三个class文件吗?PersonService$$EnhancerByCGLIB$$eaaaed75$$FastClassByCGLIB$$355cb7ea.class就是代理类的FastClass,PersonService$$FastClassByCGLIB$$a076b035.class就是被代理类的FastClass。

FastClass机制

 Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。下面我们反编译一个FastClass看看:
 //根据方法签名获取indexpublic int getIndex(Signature var1) {String var10000 = var1.toString();switch(var10000.hashCode()) {case -2077043409:if(var10000.equals("getPerson(Ljava/lang/String;)Lcom/demo/pojo/Person;")) {return 21;}break;case -2055565910:if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {return 12;}break;case -1902447170:if(var10000.equals("setPerson()V")) {return 7;}break;//省略部分代码.....  //根据index直接定位执行方法public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {eaaaed75 var10000 = (eaaaed75)var2;int var10001 = var1;try {switch(var10001) {case 0:return new Boolean(var10000.equals(var3[0]));case 1:return var10000.toString();case 2:return new Integer(var10000.hashCode());case 3:return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);case 4:return var10000.newInstance((Callback)var3[0]);case 5:return var10000.newInstance((Callback[])var3[0]);case 6:var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);return null;case 7:var10000.setPerson();return null;case 8:var10000.setCallbacks((Callback[])var3[0]);return null;case 9:return var10000.getCallback(((Number)var3[0]).intValue());case 10:return var10000.getCallbacks();case 11:eaaaed75.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);return null;case 12:eaaaed75.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);return null;case 13:return eaaaed75.CGLIB$findMethodProxy((Signature)var3[0]);case 14:return var10000.CGLIB$toString$3();case 15:return new Boolean(var10000.CGLIB$equals$2(var3[0]));case 16:return var10000.CGLIB$clone$5();case 17:return new Integer(var10000.CGLIB$hashCode$4());case 18:var10000.CGLIB$finalize$1();return null;case 19:var10000.CGLIB$setPerson$0();return null;//省略部分代码....} catch (Throwable var4) {throw new InvocationTargetException(var4);}throw new IllegalArgumentException("Cannot find matching method/constructor");}

FastClass并不是跟代理类一块生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放在了缓存中。

//MethodProxy invoke/invokeSuper都调用了init()private void init() {if(this.fastClassInfo == null) {Object var1 = this.initLock;synchronized(this.initLock) {if(this.fastClassInfo == null) {MethodProxy.CreateInfo ci = this.createInfo;MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();fci.f1 = helper(ci, ci.c1);//如果缓存中就取出,没有就生成新的FastClassfci.f2 = helper(ci, ci.c2);fci.i1 = fci.f1.getIndex(this.sig1);//获取方法的indexfci.i2 = fci.f2.getIndex(this.sig2);this.fastClassInfo = fci;this.createInfo = null;}}}}

至此,Cglib动态代理的原理我们就基本搞清楚了,代码细节有兴趣可以再研究下。最后我们总结一下JDK动态代理和Gglib动态代理的区别:1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

转载于:https://www.cnblogs.com/monkey0307/p/8328821.html

Cglib动态代理实现原理相关推荐

  1. CGLIB 动态代理及其原理分析

    一.简介   CGLIB,即 Code Generation Library,是一个强大的.高性能的代码生成库.它可以在运行期扩展 Java 类与实现 Java 接口(JDK 动态代理只能用于接口), ...

  2. Java中的原生动态代理和CGLIB动态代理的原理,我不信你全知道!

    作者:CarpenterLee cnblogs.com/CarpenterLee/p/8241042.html 动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询 ...

  3. cglib动态代理jar包_Java中的原生动态代理和CGLIB动态代理的原理,我不信你全知道!...

    作者:CarpenterLee cnblogs.com/CarpenterLee/p/8241042.html 动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询 ...

  4. JDK与CGLib动态代理实现原理

    一.代理模式概念 代理模式:给原对象提供一个代理对象,让代理对象直接控制对原对象的引用.用生活中的话来说,代理对象就是中介. 作用:保护原对象;可以增加原对象的功能. 静态代理:在编译时就获得代理对象 ...

  5. 【java】CGLIB动态代理原理分析

    1.概述 前一篇文章介绍了CGLIB中常用的API,实际上使用了Enhancer和MethodInterceptor之后会生成代理子类,这篇文章就是分析一下CGLIB动态代理的原理. 2.CGLIB动 ...

  6. 【java】CGLIB动态代理原理

    文章目录 1. 简介 2. 示例 3. 原理 4. JDK动态代理与CGLIB动态代理区别(面试常问) 1. 简介 CGLIB的全称是:Code Generation Library. CGLIB是一 ...

  7. JDK和CGLIB动态代理原理

    JDK动态代理原理解析 一.例子: 1.定义基础接口 public interface HttpApi {String get(String url); } 2.实现类 public class Re ...

  8. spring框架中JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言 JDK动态代理实现原理(jdk8):https://blog.csdn.net ...

  9. JDK和CGLIB动态代理区别

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 前言 Github:https://github.com/yihonglei/thinking-in ...

  10. Java设计模式-----Cglib动态代理(Cglib Proxy)

    接上文:4.2Java设计模式-----JDK动态代理(Dynamic Proxy) Cglib动态代理 百度百科:Cglib是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java ...

最新文章

  1. Gin 框架学习笔记(02)— 参数自动绑定到结构体
  2. IIS日志清理CMD版,VBS版,JS版,WSH版
  3. 08_传智播客iOS视频教程_点语法
  4. 算法----------快乐数 (Java版本)
  5. Python Requests快速入门
  6. java-抽象类和接口对区别
  7. Mac盖上屏幕后外接屏幕持续黑画面的解决方法
  8. html5系列:notification api升级——从webkitNotifications到Notification
  9. LabVIEW操作者框架(Actor Framework)范例集锦之三:网络搜索范例
  10. PHP7.2安装vld扩展
  11. (更新)视频设备通过rtsp接入amazon alexa echo show
  12. tmux分屏工具使用
  13. 最健康的饮料--茶的杂谈
  14. 墨西哥总统:被拆掉两次的亭子
  15. 篮球c语言程序,源程序C代码:篮球比赛应用系统
  16. 想成为顶级开发者吗?亲自动手实现经典案例
  17. 尝一尝HBuilderX香不香
  18. 1到100的和(C)
  19. 数理统计 - 圆环上随机取3个点组成一个锐角三角形的概率
  20. 【Android学习之路】之从零开始做一个小项目(一)

热门文章

  1. android apk 可以直接放在systemapp下吗,内置语音apk到/system/app下的问题
  2. mysql 记录_mysql记录耗时的sql实例详解
  3. python——学习登录用户和密码的判断——1
  4. 学习总结之 WebApi 用户登录和匿名登录,及权限验证
  5. HTML5 Media 原创翻译——第一章(持续更新中)
  6. smarty变量调节器--cat[连接字符串]
  7. 使用字符串格式化函数
  8. WinForm自适应的相关代码
  9. 活动选择问题(贪心)
  10. 重构 阅读心得(转)