<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年我写了什么?

开源编程笔记