动态代理原理解析 (二):cglib

面对没有接口的类,可以使用基于cglib的动态代理,对于final方法无法代理。

cglib的底层是使用字节码处理框架asm操纵字节码生成代理类,操纵的级别是底层JVM的汇编指令级别。

使用案例:

<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>
public class CglibTest {
    public static void main(String[] args) {
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PlayGame.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("before method run...");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("after method run...");
                return result;
            }
        });
        PlayGame sample = (PlayGame) enhancer.create();
        sample.say("hahha");
    }
}

如同Proxy,上述代码同时也生成另一个Class文件:CglibTest$1.class,实现MethodInterceptor接口。

原理

其实和Proxy的原理类似,但不同于Proxy代理类继承的是Proxy,用cglib生成的代理类继承的是被代理类,实现的是Factory接口。

我们可以在代码前加上System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://");即可以在指定位置生成代理类。它会生成三个class文件:

在这里插入图片描述
从上到下依次是1.代理类的fastclass 2.代理类 3.被代理类的fastclass
查看代理类反编译的代码:

public class PlayGame$$EnhancerByCGLIB$$74aa30cd extends PlayGame implements 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$say$0$Method;
    private static final MethodProxy CGLIB$say$0$Proxy;
    ............
	//最底下静态代码块调用这个方法初始化
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.baiye.proxy.PlayGame$$EnhancerByCGLIB$$74aa30cd");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        ........
        CGLIB$say$0$Method = ReflectUtils.findMethods(new String[]{"say", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.baiye.proxy.PlayGame")).getDeclaredMethods())[0];
        CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "say", "CGLIB$say$0");
    }

    final void CGLIB$say$0(String var1) {
        super.say(var1);
    }

    public final void say(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
        //调用拦截器
            var10000.intercept(this, CGLIB$say$0$Method, new Object[]{var1}, CGLIB$say$0$Proxy);
        } else {
            super.say(var1);
        }
    }
   }
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -1853413644:
            if (var10000.equals("say(Ljava/lang/String;)V")) {
                return CGLIB$say$0$Proxy;
            }
            break;
        case -1574182249:
            if (var10000.equals("finalize()V")) {
                return CGLIB$finalize$1$Proxy;
            }
            break;
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$5$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return CGLIB$equals$2$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$3$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$4$Proxy;
            }
        }

        return null;
    }

    public PlayGame$$EnhancerByCGLIB$$74aa30cd() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        PlayGame$$EnhancerByCGLIB$$74aa30cd var1 = (PlayGame$$EnhancerByCGLIB$$74aa30cd)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    ....

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        PlayGame$$EnhancerByCGLIB$$74aa30cd var10000 = new PlayGame$$EnhancerByCGLIB$$74aa30cd;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        default:
            throw new IllegalArgumentException("Constructor not found");
        }
    }

    .....

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK1();
    }

代理类会获得所有在父类继承来的方法,其中private MethodInterceptor CGLIB$CALLBACK_0;属性是关键,代理类持有它然后调用var10000.intercept(this, CGLIB$say$0$Method, new Object[]{var1}, CGLIB$say$0$Proxy);通过fastclass文件确定方法。

被代理类的class反编译:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.baiye.proxy;

import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;

public class PlayGame$$FastClassByCGLIB$$5f92a78b extends FastClass {
    public PlayGame$$FastClassByCGLIB$$5f92a78b(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1853413644:
            if (var10000.equals("say(Ljava/lang/String;)V")) {
                return 0;
            }
            break;
        。。。。。。
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 6;
            }
        }

        return -1;
    }

    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {
        case -1776922004:
            if (var1.equals("toString")) {
                switch(var2.length) {
                case 0:
                    return 5;
                }
            }
            break;
       。。。。。
        case 113643:
            if (var1.equals("say")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.String")) {
                        return 0;
                    }
                }
            }
            break;
        。。。。。。
        case 1950568386:
            if (var1.equals("getClass")) {
                switch(var2.length) {
                case 0:
                    return 7;
                }
            }
        }

        return -1;
    }

    public int getIndex(Class[] var1) {
        switch(var1.length) {
        case 0:
            return 0;
        default:
            return -1;
        }
    }

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        PlayGame var10000 = (PlayGame)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.say((String)var3[0]);
                return null;
            case 1:
                var10000.wait();
                return null;
            。。。。。。
            case 9:
                var10000.notifyAll();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

    public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
        PlayGame var10000 = new PlayGame;
        PlayGame var10001 = var10000;
        int var10002 = var1;

        try {
            switch(var10002) {
            case 0:
                var10001.<init>();
                return var10000;
            }
        } catch (Throwable var3) {
            throw new InvocationTargetException(var3);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

    public int getMaxIndex() {
        return 9;
    }
}

代理类的class反编译和它差不多代码过多,不在展示。

通过观察其他两个FastClass文件可以知道,通过方法索引我们可以匹配到想要的方法并且调用,而不需要像 JDK 动态代理一样通过反射的方式调用,极大提高了执行效率。它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。
这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值