关于java:设计模式33-CGLIB动态代理源码解读

27次阅读

共计 6031 个字符,预计需要花费 16 分钟才能阅读完成。

<img src=”https://markdownpicture.oss-cn-qingdao.aliyuncs.com/blog/20210925233820.png” width = “500” height = “400” alt=” 图片名称 ” align=center />

cglib 动静代理

cglib 介绍

CGLIB 是一个开源我的项目,一个弱小高性能高质量的代码生成库,能够在运行期拓展 Java 类,实现 Java 接口等等。底层是应用一个小而快的字节码解决框架 ASM,从而转换字节码和生成新的类。

实践上咱们也能够间接用 ASM 来间接生成代码,然而要求咱们对 JVM 外部,class 文件格式,以及字节码的指令集都很相熟。

这玩意不在 JDK 的包外面,须要本人下载导入或者 Maven 坐标导入。

我抉择 Maven 导入, 加到 pom.xml 文件:

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

Student.java:

public class Student {public void learn() {System.out.println("我是学生,我想学习");
    }
}

MyProxy.java(代理类)


import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class StudentProxy implements MethodInterceptor {public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("代理前 -------");
        proxy.invokeSuper(obj, args);
        System.out.println("代理后 -------");
        return null;
    }

}

测试类(Test.java


import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class Test {public static void main(String[] args) {
        // 代理类 class 文件存入本地磁盘不便咱们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/aphysia/Desktop");

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback(new StudentProxy());
        Student student = (Student) enhancer.create();
        student.learn();}
}

运行之后的后果是:

CGLIB debugging enabled, writing to '/Users/xuwenhao/Desktop'
代理前 -------
我是学生,我想学习
代理后 -------

在咱们抉择的文件夹外面,生成了代理类的代码:

源码剖析

咱们先要代理的类,须要实现 MethodInterceptor(办法拦截器) 接口, 这个接口只有一个办法 intercept, 参数别离是:

  • obj:须要加强的对象
  • method:须要拦挡的办法
  • args:要被拦挡的办法参数
  • proxy:示意要触发父类的办法对象
package net.sf.cglib.proxy;
import java.lang.reflect.Method;
public interface MethodInterceptor extends Callback {public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}

再看回咱们要创立代理类的办法 enhancer.create(), 这个办法的意思:如果须要,生成一个新类,并应用指定的回调 (如果有的话) 来创立一个新的对象实例。应用超类的无参数构造函数。

    public Object create() {
        classOnly = false;
        argumentTypes = null;
        return createHelper();}

次要的办法逻辑咱们得看 createHelper(),除了校验,就是调用 KEY_FACTORY.newInstance() 办法生成 EnhancerKey对象,KEY_FACTORY 是动态 EnhancerKey 接口,newInstance()是接口外面的一个办法,重点在 super.create(key) 外面,调用的是父类的办法:

    private Object createHelper() {
        // 校验
        preValidate();
        Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                ReflectUtils.getNames(interfaces),
                filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                callbackTypes,
                useFactory,
                interceptDuringConstruction,
                serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

AbstractClassGeneratorEnhancer 的父类,create(key) 办法的次要逻辑是获取类加载器,缓存获取类加载数据,而后再反射结构对象,外面有两个发明实例对象的办法:

  • fistInstance(): 不应该在惯例流中调用此办法。从技术上讲,{@link #wrapCachedClass(Class)}应用 {@link EnhancerFactoryData} 作为缓存值,后者反对比一般的旧反射查找和调用更快的实例化。出于向后兼容性的起因,这个办法放弃不变: 只是以防它已经被应用过。(我的了解是目前的逻辑不会走到这个分支,因为它比较忙,然而为了兼容,这个 case 还保留着),外部逻辑其实用的是ReflectUtils.newInstance(type)
  • nextInstance(): 真正的创立代理对象的类
    protected Object create(Object key) {
        try {ClassLoader loader = getClassLoader();
            Map<ClassLoader, ClassLoaderData> cache = CACHE;
            ClassLoaderData data = cache.get(loader);
            if (data == null) {synchronized (AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = cache.get(loader);
                    if (data == null) {Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                        data = new ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }
            this.key = key;
            Object obj = data.get(this, getUseCache());
            if (obj instanceof Class) {return firstInstance((Class) obj);
            }
            // 真正创建对象的办法
            return nextInstance(obj);
        } catch (RuntimeException e) {throw e;} catch (Error e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);
        }
    }

这个办法定义在 AbstractClassGenerator,然而实际上是调用子类 Enhancer 的实现,次要是通过获取参数类型,参数,以及回调对象,用这些参数反射生成代理对象。

    protected Object nextInstance(Object instance) {EnhancerFactoryData data = (EnhancerFactoryData) instance;

        if (classOnly) {return data.generatedClass;}

        Class[] argumentTypes = this.argumentTypes;
        Object[] arguments = this.arguments;
        if (argumentTypes == null) {
            argumentTypes = Constants.EMPTY_CLASS_ARRAY;
            arguments = null;
        }
        // 结构
        return data.newInstance(argumentTypes, arguments, callbacks);
    }

外部实现逻辑, 调用的都是ReflectUtils.newInstance(), 参数品种不一样:

        public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {setThreadCallbacks(callbacks);
            try {

                if (primaryConstructorArgTypes == argumentTypes ||
                        Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {return ReflectUtils.newInstance(primaryConstructor, arguments);
                }
               return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
            } finally {setThreadCallbacks(null);
            }

        }

跟进去到底,就是获取结构器办法,反射形式结构代理对象,最终调用到的是 JDK 提供的办法:

    public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {return newInstance(getConstructor(type, parameterTypes), args);
    }
    public static Object newInstance(final Constructor cstruct, final Object[] args) {boolean flag = cstruct.isAccessible();
        try {if (!flag) {cstruct.setAccessible(true);
            }
            Object result = cstruct.newInstance(args);
            return result;
        } catch (InstantiationException e) {throw new CodeGenerationException(e);
        } catch (IllegalAccessException e) {throw new CodeGenerationException(e);
        } catch (InvocationTargetException e) {throw new CodeGenerationException(e.getTargetException());
        } finally {if (!flag) {cstruct.setAccessible(flag);
            }
        }
                
    }

关上它主动生成的代理类文件看看,就会发现其实也是生成那些办法,加上了一些加强办法:

生成的代理类继承了原来的类:

public class Student$$EnhancerByCGLIB$$929cb5fe extends Student implements Factory {...}

看看生成的加强办法, 其实是调用到 intercept()办法,这个办法由咱们后面本人实现,因而就实现了代理对象加强的性能:

    public final void learn() {
        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$learn$0$Method, CGLIB$emptyArgs, CGLIB$learn$0$Proxy);
        } else {super.learn();
        }
    }

cglib 和 jdk 动静代理有什么区别

  1. jdk 动静代理是利用拦截器加上反射生成了一个代理接口的匿名类,执行办法的时候交给 InvokeHandler 解决。CGLIB 动静代理是应用了 ASM 框架,批改原来的字节码,而后生成新的子类来解决。
  2. JDK 代理须要实现接口,然而 CGLIB 不强制。
  3. 在 JDK1.6 之前,cglib 因为用了字节码生成技术,比反射效率高,然而之后 jdk 也进行了一些优化,效率上曾经晋升了。

【作者简介】
秦怀,公众号【秦怀杂货店 】作者,技术之路不在一时,山高水长,纵使迟缓,驰而不息。集体写作方向:Java 源码解析JDBCMybatisSpringredis 分布式 剑指 OfferLeetCode等,认真写好每一篇文章,不喜爱题目党,不喜爱花里胡哨,大多写系列文章,不能保障我写的都完全正确,然而我保障所写的均通过实际或者查找材料。脱漏或者谬误之处,还望斧正。

剑指 Offer 全副题解 PDF

2020 年我写了什么?

开源编程笔记

正文完
 0