美文网首页Android开发
Java CGLib代理模式实现方式以及原理分析

Java CGLib代理模式实现方式以及原理分析

作者: 巴黎没有摩天轮Li | 来源:发表于2020-08-05 21:10 被阅读0次

    代理模式的原理与实现

    在不改变原始类(被代理类)的情况下,通过引入代理类给原始类附加功能。通常情况下是让代理类与被代理类同时实现同样的接口。然而,被代理类有可能并没有定义接口,所以可以让代理类继承被代理类,重写方法来进行代理。

    动态代理的原理与实现

    静态代理需要为每个类都创建一个代理类。

    CGLib实现动态代理

    基本使用

    maveny引入
        <dependencies>
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.3.0</version>
            </dependency>
        </dependencies>
    
    被代理父类
    public class Animal {
        public void parentActivity(){
            System.out.println("Parent activity running!");
        }
    }
    
    
    被代理类
    public class Human extends Animal {
         public void activity(){
            System.out.println("Human -- Activity");
        }
    
        public void activity(String name){
            System.out.println("My name is" + name);
        }
        
        // 这里是final方法 无法被代理
        public final void activity(String name, int age){
            System.out.println("My name is" + name +" and I'm " + age + " years old.");
        }
    }
    
    代理方法拦截器
    public class HumanInterceptor implements MethodInterceptor {
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("PROXY_BEFORE");
            // 通过非反射调用被代理方法
            Object i = methodProxy.invokeSuper(o, objects);
            System.out.println("PROXY_AFTER");
            return i;
        }
    }
    
    通过CGLib#Enhance生成代理类
    public static void main(String[] args) {
            // 动态代理日志文件
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java\\java_workapace");
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Human.class);
            enhancer.setCallback(new HumanInterceptor());
            Human human = (Human)enhancer.create();
            human.activity();
            human.activity("EdisonLi");
        }
    
    执行Main方法
    /******被代理方法执行******/
    PROXY_BEFORE
    Human -- Activity
    PROXY_AFTER
    /******代理失败******/
    My name isEdisonLi
    

    有结果可见,GCLib实现的代理方法,若方法是final的,则无法被代理。因此我们有理由猜想,GCLib是通过继承的方式进行的代理,我们来验证下。
    我们用Jad来反编译下.class文件。



    通过Jad反编译
    获取反编译后的java代码
    public class Human$$EnhancerByCGLIB$$959f8f96 extends Human implements Factory{
          // 省略所有 后文慢慢来说明
    }
    

    由此可见,生成的代理类其实是继承于被代理类的,这里的被代理类是Human。

    public class Human$$EnhancerByCGLIB$$959f8f96 extends Human 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$activity$0$Method;
       private static final MethodProxy CGLIB$activity$0$Proxy;
       private static final Object CGLIB$emptyArgs[];
       private static final Method CGLIB$activity$1$Method;
       private static final MethodProxy CGLIB$activity$1$Proxy;
       private static final Method CGLIB$parentActivity$2$Method;
       private static final MethodProxy CGLIB$parentActivity$2$Proxy;
       private static final Method CGLIB$equals$3$Method;
       private static final MethodProxy CGLIB$equals$3$Proxy;
       private static final Method CGLIB$toString$4$Method;
       private static final MethodProxy CGLIB$toString$4$Proxy;
       private static final Method CGLIB$hashCode$5$Method;
       private static final MethodProxy CGLIB$hashCode$5$Proxy;
       private static final Method CGLIB$clone$6$Method;
       private static final MethodProxy CGLIB$clone$6$Proxy;
    
       static {
            // 静态代码块调用初始化方法
            CGLIB$STATICHOOK1();
        }
    
        // 初始化一些参数
       static void CGLIB$STATICHOOK1()
        {
            Method amethod[];
            Method amethod1[];
            Method amethod2[];
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            // 1
            Class class1 = Class.forName("main.java.Human$$EnhancerByCGLIB$$959f8f96");
            Class class2;
            // 2
            amethod = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"
            }, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());
            Method[] _tmp = amethod;
            CGLIB$equals$3$Method = amethod[0];
            CGLIB$equals$3$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
            CGLIB$toString$4$Method = amethod[1];
            CGLIB$toString$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
            CGLIB$hashCode$5$Method = amethod[2];
            CGLIB$hashCode$5$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$5");
            CGLIB$clone$6$Method = amethod[3];
            CGLIB$clone$6$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
            // 3
            amethod1 = ReflectUtils.findMethods(new String[] {
                "activity", "(Ljava/lang/String;)V", "activity", "()V"
            }, (class2 = Class.forName("main.java.Human")).getDeclaredMethods());
            Method[] _tmp1 = amethod1;
            CGLIB$activity$0$Method = amethod1[0];
            CGLIB$activity$0$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/String;)V", "activity", "CGLIB$activity$0");
            CGLIB$activity$1$Method = amethod1[1];
            CGLIB$activity$1$Proxy = MethodProxy.create(class2, class1, "()V", "activity", "CGLIB$activity$1");
            // 4
            amethod2 = ReflectUtils.findMethods(new String[] {
                "parentActivity", "()V"
            }, (class2 = Class.forName("main.java.Animal")).getDeclaredMethods());
            Method[] _tmp2 = amethod2;
            CGLIB$parentActivity$2$Method = amethod2[0];
            CGLIB$parentActivity$2$Proxy = MethodProxy.create(class2, class1, "()V", "parentActivity", "CGLIB$parentActivity$2");
        }
    }
    

    注释1处获取代理类的字节码Class,注释2通过ReflectUtils#findMethods()获取Object类中的方法,其最终目的就是验证下目标类是否具有所要的方法数组,并且为以后的FastClass做准备。

    ReflectUtils#findMethods()

    public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods) {
            Map map = new HashMap();
    
            // 遍历类中定义的方法数组 以方法名+方法描述符作为Map的Key, 相当于为每个被代理类的方法建立方法索引,当需要调用类中的方法的时候就直接根据方法索引直接调用到目标方法。
            for(int i = 0; i < methods.length; ++i) {
                Method method = methods[i];
                map.put(method.getName() + Type.getMethodDescriptor(method), method);
            }
    
            Method[] result = new Method[namesAndDescriptors.length / 2];
    
            for(int i = 0; i < result.length; ++i) {
                // 根据namesAndDescriptors找到方法数组并返回
                result[i] = (Method)map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]);
                if (result[i] == null) {
                }
            }
            return result;
        }
    

    注释3处,是寻找对应被代理类两个activity方法数组,并针对每一个方法数组根据对应的index进行创建方法代理,注释4同理是针对被代理类父类的非final方法同样的操作。

    MethodProxy#create(...)

    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;
        }
    

    通过create()方法实例化了MethodProxy类,参数c1是被代理类,参数c2是代理类,参数name1是被代理方法名称,参数name2是被代理方法名称,参数desc是之前提到的存储Map中的key值。通过这些参数又构造出MethodProxy的内部类CreateInfo类,并赋值给MethodProxy中的成员变量createInfo,为后续在拦截器中调用MethodProxy#invokeSuper()方法时使用。
    我们回到代理类中,我们以其中一个方法调用的例子来分析代理方法的调用流程。

    public class Human$$EnhancerByCGLIB$$959f8f96 extends Human implements Factory{
         // 1
        // 方法拦截器
        private MethodInterceptor CGLIB$CALLBACK_0;
    
        // 设置方法拦截器
        public void setCallback(int i, Callback callback)
        {
            switch(i)
            {
            case 0: // '\0'
                CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
                break;
            }
        }
    
        final void CGLIB$activity$0(String s)
        {
            super.activity(s);
        }
    
        public final void activity(String s)
        {
            CGLIB$CALLBACK_0;
            // 2
            if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
    _L1:
            JVM INSTR pop ;
            CGLIB$BIND_CALLBACKS(this);
            CGLIB$CALLBACK_0;
    _L2:
            JVM INSTR dup ;
            JVM INSTR ifnull 42;
               goto _L3 _L4
    _L3:
            break MISSING_BLOCK_LABEL_21;
    _L4:
            break MISSING_BLOCK_LABEL_42;
            // 3
            this;
            CGLIB$activity$0$Method;
            new Object[] {
                s
            };
            CGLIB$activity$0$Proxy;
            intercept();
            return;
            super.activity(s);
            return;
        }
    }
    

    注释1处代码,对方法拦截器MethodInterceptor进行set get方法,之后在注释2处调用代理方法的时候先去判断一下方法拦截器是否为空,若不为空最终会到_L4方法中,注释3处的代码调用MethodInterceptor#intercept()方法并传入参数。看到这里我们知道了调用代理方法最终会调用拦截器中的interceptor的方法。之后我们看看如何调用被代理类的方法的。

    public class HumanInterceptor implements MethodInterceptor {
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {   
            // 通过非反射调用被代理方法
            Object i = methodProxy.invokeSuper(o, objects);
            return i;
        }
    }
    

    来到方法拦截器中,我们看下最终调用原始方法是通过MethodProxy.invokeSuper()方法进行调用的。

    // FastClassInfo 包含代理类与非代理类的FastClass
     private static class FastClassInfo {
            FastClass f1;   //  被代理类的FastClass
            FastClass f2;  // 代理类的FastClass
            int i1;  // 被代理类的方法索引
            int i2; // 代理类的方法索引
    
            private FastClassInfo() {
            }
        }
    
       public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                // 1
                this.init();
                // FastClass
                MethodProxy.FastClassInfo fci = this.fastClassInfo;
                // 2
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException var4) {
                throw var4.getTargetException();
            }
        }
    

    代码1处进行生产FastClass,FastClass作用就是不使用反射进行调用被代理类中的原始方法,而是采用索引的方式找到被代理的原始方法并进行调用,方法的索引的建立就在init()方法之中。代码2 处通过索引进行原始方法的调用。

    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();
                        // 1
                        fci.f1 = helper(ci, ci.c1);
                        fci.f2 = helper(ci, ci.c2);
                        // 2
                        fci.i1 = fci.f1.getIndex(this.sig1);
                        fci.i2 = fci.f2.getIndex(this.sig2);
                        this.fastClassInfo = fci;
                        this.createInfo = null;
                    }
                }
            }
        }
    

    init()方法中注释1处的代码是对FastClass类进行生成。注释2处的代码是根据具备方法名称与别名的Signature的hashcode值作为方法的索引,我们反编译一下生成的FastClass类。

    public class Human$$EnhancerByCGLIB$$959f8f96$$FastClassByCGLIB$$51effc24 extends FastClass {
     public int getIndex(Signature signature)
        {
            String s = signature.toString();
            s;
            s.hashCode();
            JVM INSTR lookupswitch 26: default 508
               goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L8 _L9 _L10 _L11 _L12 _L13 _L14 _L15 _L16 _L17 _L18 _L19 _L20 _L21 _L22 _L23 _L24 _L25 _L26 _L27
    _L7:
            "activity(Ljava/lang/String;)V";
            equals();
            JVM INSTR ifeq 509;
               goto _L38 _L39
    _L39:
            break MISSING_BLOCK_LABEL_509;
    _L38:
            return 7;
    _L8:
            "activity()V";
            equals();
            JVM INSTR ifeq 509;
               goto _L40 _L41
    _L41:
            break MISSING_BLOCK_LABEL_509;
    _L40:
            return 8;
    _L1:
            JVM INSTR pop ;
            return -1;
        }
    
    }
    

    根据方法哈希值对应响应的方法别名最终返回一个index值,然后再调用FastClass的invoke方法将index值传入,在该方法中,因为是继承关系,根据index值直接调用原始方法,规避了反射调用带来的性能损失。

    public Object invoke(int i, Object obj, Object aobj[])
            throws InvocationTargetException
        {
            (Human..EnhancerByCGLIB.._cls959f8f96)obj;
            i;
            JVM INSTR tableswitch 0 25: default 383
        //                   0 124
        //                   1 139
        //                   2 143
        //                   3 155
        //                   4 159
        //                   5 169
        //                   6 191
        //                   7 201
        //                   8 212
        //                   9 217
        //                   10 228
        //                   11 233
        //                   12 253
        //                   13 264
        //                   14 275
        //                   15 288
        //                   16 292
        //                   17 307
        //                   18 312
        //                   19 323
        //                   20 327
        //                   21 339
        //                   22 343
        //                   23 353
        //                   24 358
        //                   25 363;
               goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L8 _L9 _L10 _L11 _L12 _L13 _L14 _L15 _L16 _L17 _L18 _L19 _L20 _L21 _L22 _L23 _L24 _L25 _L26 _L27
    _L8:
            (Callback)aobj[0];
            newInstance();
            return;
    _L9:
            (String)aobj[0];
            activity();
            return null;
    _L10:
            activity();
            return null;
    _L19:
            CGLIB$activity$1();
            return null;
    _L20:
            (String)aobj[0];
            CGLIB$activity$0();
            return null;
        public Human$$EnhancerByCGLIB$$959f8f96$$FastClassByCGLIB$$51effc24(Class class1)
        {
            super(class1);
        }
    }
    

    说到这里CGLib的动态代理分析到这里就结束了,还有些细节比如如何通过ASM进行字节码插桩编译,这里就不详细说了。

    相关文章

      网友评论

        本文标题:Java CGLib代理模式实现方式以及原理分析

        本文链接:https://www.haomeiwen.com/subject/dtflrktx.html