CGLIB代理使用示例与底层原理解析

CGLIB经常使用使用示例:html

目标类java

public class Todo {
    public void doString(String desc) {
        System.out.println("doString: " + desc);
    }
}

代理生成类 编程

public class ProxyInstance implements MethodInterceptor {




    public <T> T getInstance(Class<T> klass) {
        Enhancer enhancer = new Enhancer();
        /*
         * 设置父类型,用于生成代理的子类
         */
        enhancer.setSuperclass(klass);
        /*
         * 设置实现Callback接口的实例对象
         * 这里为MethodInterceptor对象
         */
        enhancer.setCallback(this);
        /*
         * create()用于建立代理类对象
         * createClass()用于生成java字节码
         */
        return (T) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("proxy before");
        Object obj= methodProxy.invokeSuper(o, objects);
        System.out.println("proxy after");
        return obj;
    }
}

测试类app

public class Test {
    public static void main(String[] args) {
        String path = Test.class.getClassLoader().getResource("").getPath();
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path + "cglib");
        Todo todo=new ProxyInstance().getInstance(Todo.class);
        todo.doString("------ nothing-------- ");
    }
}

控制台框架

CGLIB debugging enabled, writing to '/E:/ideaspace/demo/demo/target/classes/cglib'
proxy before
doString: ------ nothing--------
proxy after

ASM(assembly):汇编的缩写,是java的一个字节码级别的编程框架,它能够动态生成Class的字节码文件,CGLIB以及Groovy等都是基于ASM实现动态语言特性的,在CGLIB的代理中主要用于生成类文件,实现方式为动态生成继承了目标类的代理类。maven

maven依赖ide

<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>8.0.1</version>
</dependency>

针对示例的目标类,生成类过多这里只展现主要代码:注意全部生成的类都会加载到JVM中测试

// 代理类相关的生成类this

Todo$$EnhancerByCGLIB$$939b412c$$FastClassByCGLIB$$b6c5bc7f.class
Todo$$EnhancerByCGLIB$$939b412c.class  // 代理类
Todo$$FastClassByCGLIB$$9f944a58.class

//其余类url

core/MethodWrapper$MethodWrapperKey$$KeyFactoryByCGLIB$$d45e49f7.class
proxy/Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$7fb24d72.class

代理类描述

// 经过继承方式来实现代理,目标类成为了父类supper。经过继承实现也附带了继承的缺点,注意final类型和final方法的坑

public class Todo$$EnhancerByCGLIB$$939b412c extends Todo implements Factory {...}

针对方法的静态处理

static void CGLIB$STATICHOOK1() {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    // 当前代理类
    Class var0 = Class.forName("cn.tinyice.demo.proxy.cglib.Todo$$EnhancerByCGLIB$$939b412c");
    Class var1;
    // 经过反射获取java.lang.Object声明的全部方法(不包括继承的方法,这里也不会有)中的equals、toString、hashCode、clone方法,准备重写
    // 注意 这里 var1赋值  
    Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 =         Class.forName("java.lang.Object")).getDeclaredMethods());
    // 对每一个方法获取其Method对象$Method以及代理对象$Proxy
    CGLIB$equals$1$Method = var10000[0];
    CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
    CGLIB$toString$2$Method = var10000[1];
    CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
    CGLIB$hashCode$3$Method = var10000[2];
    CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
    CGLIB$clone$4$Method = var10000[3];
    CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    // 获取目标类用户方法doString的Method对象$Method以及代理对象$Proxy,注意这里var1赋值
    CGLIB$doString$0$Method = ReflectUtils.findMethods(new String[]{"doString", "(Ljava/lang/String;)V"}, (var1 = Class.forName("cn.tinyice.demo.proxy.cglib.Todo")).getDeclaredMethods())[0];
    CGLIB$doString$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "doString", "CGLIB$doString$0");
}

代理方法描述

public final void doString(String var1) {
    // 方法拦截器:从callBack中获取,这里为ProxyInstance
    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$doString$0$Method, new Object[]{var1}, CGLIB$doString$0$Proxy);
    } else {
    // 不拦截方法则直接调用原方法
        super.doString(var1);
    }
}

到这里已经执行到了ProxyInstance#intercept,而后调用methodProxy.invokeSuper(o, objects);

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        // 初始化MethodProxy实例,注意在调用时才初始化的
        this.init();
        // 获取FastClass
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        // 调用FastClassInfo的FastClass2的invoke,参数为方法签名下标,调用对象、方法参数
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throw var4.getTargetException();
    }
}


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();
                // fastClass1 生成 ,c1为MethodProxy.create的第一个参数,目标类
                              // 生成类名称前缀=参数2的类名称+$$FastClassByCGLIB$$Integer.toHexString(hashCode)
                fci.f1 = helper(ci, ci.c1);
                // fastClass2生成,c2为MethodProxy.create的第二个参数,代理类Class
                fci.f2 = helper(ci, ci.c2);
                // 调用getIndex获取下标,参数为方法签名,两者一一映射,这里使用switch语句来提升效率
                fci.i1 = fci.f1.getIndex(this.sig1);
                fci.i2 = fci.f2.getIndex(this.sig2);
                // 实例构建完毕
                this.fastClassInfo = fci;
                this.createInfo = null;
            }
        }
    }
}
法拦截器中的调用 : fci.f2.invoke(fci.i2, obj, args)
 
目标类$$EnhancerByCGLIB$$FastClassByCGLIB$$Integer.toHexString(hashCode).invoke(代理类的方法签名、传入代理对象,传入的方法参数)
调用时一样使用switch语句来调用对应的方法
 
 
总结:
 
经过Enhancer能够生成代理类,传入CallBack使代码执行时进入CallBack代码中,从而执行用户自定义代码;
用户自定义代码能够决定使用目标类调用仍是代理类调用
  • 目标类使用$$FastClass1进行调用
  • 代理类使用$$FastClass2进行调用
  • $$FastClass都在调用时触发生成
$$FastClass为动态生成类,经过index与方法签名一一映射产生switch case 语句来提升调用效率
代理类是经过继承方式实现,不能对final类和方法使用CGLIB代理。
 
所以 CGLB代理调用效率高,可是生成效率慢,至少3个文件,1个代理类,2个FastClass类。全部生成的类都会加载到JVM堆中。
 
相关文章
相关标签/搜索