动态代理-cglib分析
生成代理类文件的方式
jvm添加此启动参数,后面就是代理类class生成的地址
-Dcglib.debugLocation=~/baldhead/java/dynamic-proxy-cglib/src/main/java/com/baldhead/dynamic/proxy/cglib/class
添加这个参数之后,CGLIB就会把生成的代理Class文件存在指定的路径
生成动态代理对象流程
- CGLIB首先生成代理类
- 代码中的 static 静态代码块 会调用 CGLIB$STATICHOOK1();方法,方法作用
 3. 新建一个名字为CGLIB$THREAD_CALLBACKS的ThreadLocal,用来存放所设置的callback
 4. 使用反射找到代理类中的所有方法,包括(toString、hashCode、equals、clone),名字为模板CGLIB$METHODNAME$数字编号$Method
 并且给对应的方法创建代理方法 名字模板CGLIB$METHODNAME$数字编号$Proxy
- 调用构造方法创建代理对象
- 然后CGLIB会调用代理对象的 CGLIB$SET_THREAD_CALLBACKS方法,将传入的callBack存到ThreadLocal(CGLIB$THREAD_CALLBACKS)中去
- 后续在对象执行需要代理的方法的时候,就会从CGLIB$THREAD_CALLBACKS中拿到所设置的CallBack并调用它的intercept()方法
代理对象的创建
static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService$$EnhancerByCGLIB$$e34eec9a");
        Class var1;
        CGLIB$test$0$Method = ReflectUtils.findMethods(new String[]{"test", "()V"}, (var1 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService")).getDeclaredMethods())[0];
        CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
    }
以上代码经过简化的,主要看下面给出的一行
CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
对应的方法如下
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        /**
         * 这几个参数都可以找到入参对象
         * c1: 被代理类对象的class,也就是原始对象的class
         * c2: 代理类对象的 class
         * desc: 方法的返回值类型
         * name1: 原始代理方法的名称
         * name2: 代理方法在代理类中的名称(CGLIB$test$0)
         */
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new CreateInfo(c1, c2);
        return proxy;
    }
在MethodProxy中有三个很重要的属性
- sig1: 表示test方法
- sig2: 表示 CGLIB$test$0 方法
- createInfo: 表示原始类和代理类
invoke和invokeSuper方法
 public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            FastClassInfo fci = this.fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            if (this.fastClassInfo.i1 < 0) {
                throw new IllegalArgumentException("Protected method: " + this.sig1);
            } else {
                throw var5;
            }
        }
    }
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
两个方法大差不差的,但是都用到了一个对象  fastClassInfo 这个对象是在 init()方法中构造的
private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    CreateInfo ci = this.createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }
    }
fastClassInfo对象中主要是有四个属性
- f1: 原始类对应的一个FastClass 代理对象
- f2: 代理类对应的一个FastClass 代理对象
- i1: test方法在原始类对应的一个FastClass代理对象中的下标
- i2: CGLIB$test$0方法在代理类对应的一个 FastClass 代理对象中的下标
 这里产生了两个代理对象,你说好巧不巧,正好产生的代理,class有3个,其中有两个继承 FastClass, 另外一个继承原始类并且实现 Factory接口

其实这两个类类似,都是针对某一个类的FastClass代理类,所以我们好好看一下UserService所对应的FastClass该类主要有:
- 一个构造方法
- public int getlndex(Signature var1)
- public int getlndex(String var1, Classll var2)
- public int getlndex(ClassI var1)
- public Object invoke(int var1, Object ar2, Objectll var3)
- public Object newlnstance(int var1, Objectll var2)
- public int getMaxlndex0
顾名思义,FastClass的作用是提高方法的执行速度,按照正常的实现,当我们调用MethodProxy对象的invokel或invokeSuper0方法时,首先应该要做到的就是找到对应的Method对象,比如:
- 执行invoke0,要找到test方法对应的Method对象 
- 执行invokeSuper0,要找到CGLIBstest$00方法对应的Method对象然后利用反射来执行Method。 
那么FastClass的机制就是预先把UserService类或UserService代理类中的所有方法做一个索引,比如:
  public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch (var10000.hashCode()) {
            case -2055565910:
                if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                    return 19;
                }
                break;
            case -1659690448:
                if (var10000.equals("CGLIB$test$4()V")) {
                    return 20;
                }
                break;
            case -1457535688:
                if (var10000.equals("CGLIB$STATICHOOK1()V")) {
                    return 12;
                }
                break;
            case -1422510685:
                if (var10000.equals("test()V")) {
                    return 7;
                }
                break;
            case -1411872516:
                if (var10000.equals("CGLIB$hashCode$2()I")) {
                    return 15;
                }
                break;
        // 省略部分代码
        }
        return -1;
    }
一旦调用 getIndex(Signature var1) 方法,就对得到对应方法返回的索引,例如这里就是test方法返回的对应的索引就是7
再回到init 方法
private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    CreateInfo ci = this.createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }
    }
init方法中的两个 helper方法就是去生成原始类和代理类的 FactClass代理类,后面个两个getIndex方法
1. 第一个fci.f1.getIndex(this.sig1)就是去获取原始类对应的FastClass代理类中 test方法的下标i1
2. 第二个 fci.f2.getIndex(this.sig2)就是去获取代理类对应的FastClass代理类中$test$0方法的下标i2
然后会把两个下标都记录在 fastClassInfo 对象中
后面就是我们看到的invoke和invokeSuper中调用的两个方法
- invoke - fci.f1.invoke(fci.i1, obj, args);- 执行原始类对应的FastClass 代理类的invoke方法 
 
- invokeSuper - fci.f2.invoke(fci.i2, obj, args);- 执行代理类对应的 - FastClass代理类的- invoke方法
 
例如: 原始类对应的FastClass 代码
 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        UserService var10000 = (UserService)var2;
        int var10001 = var1;
        try {
            switch (var10001) {
                case 0:
                    var10000.test();
                    return null;
                case 1:
                    return new Boolean(var10000.equals(var3[0]));
                case 2:
                    return var10000.toString();
                case 3:
                    return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
这个代码比较简单,第一个参数就是执行方法的index,第二个参数就是原始类,第三个就是原始类的参数
如果传入的index 是0 ,那么就会去执行test方法
代理类对应的FastClass代理类的invoke方法也是类似
 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        UserService..EnhancerByCGLIB..e34eec9a var10000 = (UserService..EnhancerByCGLIB..e34eec9a)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.clone();
                case 4:
                    return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
                case 5:
                    return var10000.newInstance((Callback[])var3[0]);
                case 6:
                    return var10000.newInstance((Callback)var3[0]);
                case 7:
                    var10000.test();
                    return null;
                case 8:
                    e34eec9a.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 9:
                    e34eec9a.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 10:
                    var10000.setCallbacks((Callback[])var3[0]);
                    return null;
                case 11:
                    return var10000.getCallback(((Number)var3[0]).intValue());
                case 12:
                    return var10000.getCallbacks();
                case 13:
                    var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                    return null;
                case 14:
                    return e34eec9a.CGLIB$findMethodProxy((Signature)var3[0]);
                case 15:
                    e34eec9a.CGLIB$STATICHOOK1();
                    return null;
                case 16:
                    var10000.CGLIB$test$0();
                    return null;
                case 17:
                    return new Integer(var10000.CGLIB$hashCode$3());
                case 18:
                    return new Boolean(var10000.CGLIB$equals$1(var3[0]));
                case 19:
                    return var10000.CGLIB$toString$2();
                case 20:
                    return var10000.CGLIB$clone$4();
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
例如传入的index 是16 那么执行的就是 var10000.CGLIB$test$0();
如果传入的index是 7 那么执行的就是var10000.test();
var10000 是传入对象强转为UserService..EnhancerByCGLIB..e34eec9a类的对象,UserService..EnhancerByCGLIB..e34eec9a类其实就是UserService的代理类
invokeSuper结论
所以当我们执行invokeSuper方法的时候,不能传入原始类(UserService)只能传入代理类对象,不然就无法转换成为代理类类型
所以FastClass 快的地方就是预先把所有的方法信息都生成了对应的index,在真正的去执行的时候不用再去找Method对象,直接传入对应方法的index就可以直接执行对应的方法了
动态代理-cglib分析的更多相关文章
- Java代理和动态代理机制分析和应用
		本博文中项目代码已开源下载地址:GitHub Java代理和动态代理机制分析和应用 概述 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息 ... 
- Proxy 代理模式 动态代理 cglib MD
		Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ... 
- Java 动态代理机制分析及扩展
		Java 动态代理机制分析及扩展,第 1 部分 王 忠平, 软件工程师, IBM 何 平, 软件工程师, IBM 简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟 ... 
- 获取JDK动态代理/CGLIB代理对象代理的目标对象。
		问题描述:: 我现在遇到个棘手的问题,要通过spring托管的service类保存对象,这个类是通过反射拿到的,经过实验发现这个类只能反射取得sservice实现了接口的方法,而extends类的方法 ... 
- java:java静态代理与动态代理简单分析
		java静态代理与动态代理简单分析 转载自:http://www.cnblogs.com/V1haoge/p/5860749.html 1.动态代理(Dynamic Proxy) 代理分为静态代理和动 ... 
- Java 动态代理机制分析及扩展,第 1 部分
		Java 动态代理机制分析及扩展,第 1 部分 http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/ 本文通过分析 Java 动态代理的机制和特 ... 
- 基于JDK的动态代理原理分析
		基于JDK的动态代理原理分析 这篇文章解决三个问题: What 动态代理是什么 How 动态代理怎么用 Why 动态代理的原理 动态代理是什么? 动态代理是代理模式的一种具体实现,是指在程序运行期间, ... 
- cglib源码分析(四):cglib 动态代理原理分析
		本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ... 
- 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)
		代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ... 
- Java动态代理全面分析
		代理模式 解说:给某一个对象提供一个代理,并由代理对象控制对原对象的引用: 代理模式需要以下几个角色: 1 主题:规定代理类和真实对象共同对外暴露的接口: 2 代理类:专门代理真实对象的类: 3 ... 
随机推荐
- LAPM概述及配置
			一.LAMP概述 1.1LAMP的概念 LAMP架构是目前成熟的企业网站应用模式之一,指的是协同工作的一整套系统和相关软件,能够提供动态web站点服务及其应用开发环境 LAMP是一个缩写词,具体包括L ... 
- OpenMP 教程(一) 深入人剖析 OpenMP reduction 子句
			OpenMP 教程(一) 深入人剖析 OpenMP reduction 子句 前言 在前面的教程OpenMP入门当中我们简要介绍了 OpenMP 的一些基础的使用方法,在本篇文章当中我们将从一些基础的 ... 
- ClickHouse(10)ClickHouse合并树MergeTree家族表引擎之ReplacingMergeTree详细解析
			目录 建表语法 数据处理策略 资料分享 参考文章 MergeTree拥有主键,但是它的主键却没有唯一键的约束.这意味着即便多行数据的主键相同,它们还是能够被正常写入.在某些使用场合,用户并不希望数据表 ... 
- ModuleNotFoundError: No module named 'XXX'
			先来一张表情包: pycharm在小黑框使用pip安装某个包,在解释器没有找到某个包,所以运行程序的时候总是报错. 我相信大家可能都遇到这样的问题. 我下载有3.8.3.10版本的python,我py ... 
- fuzor2020安装教程
			fuzor下载安装包fuzor2020安装教程Fuzor2020 WIN10 64位安装步骤:1.先使用"百度网盘客户端"下载Fur20_CN_x64安装包到电脑磁盘里,并鼠标右击 ... 
- Java新特性(2):Java 10以后
			您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来- 虽然到目前为止Java的版本更新还没有什么惊天动地的改变,但总是会冒出一些有趣的小玩意.前面列举了Java9和Java10的一些特色,现在接着来 ... 
- Bugku login1
			打开是个普普通通的登录界面,盲猜是注入题,先看看源码吧,没找到什么有用的信息,那就先注册试试 注册admin就已经存在,可能待会就爆破admin的密码也可能,因为没有验证嘛 试试注册其他的 登录发现他 ... 
- C++初阶(stack+queue)
			stack stack介绍 stack是一种先进后出的数据结构,只有一个出口,类似于栈.stack容器哦允许新增元素,移除元素,取得栈顶元素,但是除了最顶端之后,没有任何其他办法可以存取stack的其 ... 
- day27  CSS浮动、溢出 & js基本语法 & DOM文档流操作
			接day26CSS=>CSS定位 overflow属性 值 描述 示例 visible 默认值,内容不会被修剪,会呈现在元素框之外 hidden 内容会被修剪,并且其余内容是不可见的 overf ... 
- WEB入门——爆破21-28
			WEB21 首先尝试网站后台常见登陆的弱口令,发现错误 则使用burp suite抓包试试看 通过分析,在未填入账号密码时,响应头如下所示: 填入弱口令账号密码,发现响应头如下: 则对应可知账号密 ... 
