Java 代理学习笔记 —— 浅析Cglib动态代理实现

本文详细介绍了Cglib动态代理的实现机制,包括如何通过继承委托类生成代理类,Cglib的FastClass在减少反射调用时间中的作用,以及Cglib代理与Java原生代理的差异。通过示例代码展示了Cglib代理的创建和工作原理,揭示了Cglib在生成代理对象和方法调用时的文件生成过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简述

上一篇博客中,我们仔细地分析了Java原生代理的实现机制,得出了如下结论
- 代理的类动态生成的。也就是说,在程序运行时创建类的字节码,并且动态加载到JVM中
- Java原生代理实际上是创造了一个实现了所有给定接口的类,并在调用接口的方法的时候使用反射达到代理效果
基于以上两点,我们能得出结论,如果要使用Java原生代理的话,就必须将类的每一个方法都写在接口里面再进行实现。而Cglib的出现,就解决了这个问题。在Cglib Github主页上我们能看到如此描述

Cglib(Code Generation Library) 能够提供生成和转换Java字节码的API,它被广泛运用在AOP,测试,数据处理以及生成动态代理和方法拦截的框架中。

接下来,我们看一下Cglib是如何工作的。

Cglib Proxy 示例

对于Cglib的代理对象,与Java原生代理对象相同,它们都需要生成一个代理类对代理对象进行持有,同时必须在生成这个代理类的时候,将一个回调方法处理类作为参数传入构造函数中。这样,当代理类的任意方法被调用的时候,Java原生代理会调用InvocationHandler中的invoke方法,而Cglib代理则会调用MethodInterceptor中的intercept方法。
仍然以前面博客中的UserDao以及UserDaoImpl为例进行代码示例。
SimpleMethodInterceptor.java

package com.study.cglib.learning;

import java.lang.reflect.Method;

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

public class SimpleMethodInterceptor implements MethodInterceptor{

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("proxy control!");
        //注意,如果使用invoke方法,并且参数也为代理类,则会循环进入此拦截方法从而导致爆栈
        //invokeSuper即为调用原生的方法
        return proxy.invokeSuper(obj, args);
    }

    public Object createProxy(Class<?> clazz, Class<?>[] interfaces) {
        Enhancer enhancer = new Enhancer();
        //设置需要代理的类
        enhancer.setSuperclass(clazz);
        //设置需要代理的接口
        enhancer.setInterfaces(interfaces);
        //设置处理方法的回调函数
        enhancer.setCallback(this);
        //设置回掉函数的过滤方法
        enhancer.setCallbackFilter(new SimpleCallBackfilter());
        return enhancer.create();
    }
}

SimpleCallBackfilter.java

public class SimpleCallBackfilter implements CallbackFilter{

    @Override
    public int accept(Method method) {
        System.out.println("filtering!");
        return 0;
    }

}

我们可以为代理类设置多个回调方法(将回调类以数组形式传入setCallBacks方法中),然后再设置callBackFilter,callBackFilter返回的整型代表着第几个回调类的拦截方法将会被调用。

App.java

package com.study.cglib.learning;

public class App 
{
    public static void main( String[] args )
    {
        SimpleMethodInterceptor cgProxy = new SimpleMethodInterceptor();

        UserDao userDao = (UserDao) cgProxy.createProxy(UserDaoImpl.class, null);

        userDao.queryName();
        //输出 : filtering!
        //      filtering!
        //      filtering!
        //      filtering!
        //      filtering!
        //      filtering!
        //      proxy control!
        //      getUserName
    }
}

浅析Cglib生成代理类的方式

Enhancer.class通过继承AbstractClassGenerator,并且实现了generateClass来进行代理类的字节码动态生成,其方式和过程与Java原生代理类似,找到所有声明的方法,构建构造函数,构建代理函数,最后根据内存中生成的所有函数,拼凑成字节码。唯一不同的是,Cglib**直接继承**委托类。而CallBackFilter实际上也是在生成函数的时候生效的,如下所示。
Enhancer.emitMethods

while (it1.hasNext()) {
            MethodInfo method = (MethodInfo)it1.next();
            Method actualMethod = (it2 != null) ? (Method)it2.next() : null;
            //通过callbackfilter确定目前的方法将会被哪个callback拦截
            int index = filter.accept(actualMethod);
            if (index >= callbackTypes.length) {
                throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index);
            }
            ...
        }

Cglib之FastClass

与Java原生代理类似,我们也能够通过某个系统变量来保存Cglib产生的class文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib");
但是,与原生代理类不同的是,如果生成了代理类并且进行了方法调用,将会生成三个class文件。
UserDaoImpl$$EnhancerByCGLIB$$c1115585$$FastClassByCGLIB$$d2f68b9b.class
UserDaoImpl$$EnhancerByCGLIB$$c1115585.class
UserDaoImpl$$FastClassByCGLIB$$e8aae1d6.class
如果只看文件名的话,发现两个是属于FastClass,而一个属于Enhancer,我们先观察Enhancer class。这次我们通过另外一个decompiler工具procyon来查看生成的文件
java -jar procyon-decompiler-0.5.30.jar UserDaoImpl$$EnhancerByCGLIB$$c1115585.class > UserDaoImpl$$EnhancerByCGLIB$$c1115585.class.java

// 
// Decompiled by Procyon v0.5.30
// 

package com.study.cglib.learning;

import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;

public class UserDaoImpl$$EnhancerByCGLIB$$c1115585 extends UserDaoImpl implements Factory
{
    //与Java原生代理类似,仍然以静态变量保存了指向代理方法的引用
    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$queryName$0$Method;
    private static final MethodProxy CGLIB$queryName$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    //每个函数有两个反射方法,一个是通过原生反射获得的,另外一个是通过Cglib构建的。
    private static final Method CGLIB$hashCode$1$Method;
    private static final MethodProxy CGLIB$hashCode$1$Proxy;
    //...

    //在类加载的时候,初始化所有静态变量
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        final Class<?> forName = Class.forName("com.study.cglib.learning.UserDaoImpl$$EnhancerByCGLIB$$c1115585");
        final Class<?> forName2;
        final Method[] methods = ReflectUtils.findMethods(new String[] { "hashCode", "()I", "equals", "(Ljava/lang/Object;)Z", "clone", "()Ljava/lang/Object;", "toString", "()Ljava/lang/String;", "finalize", "()V" }, (forName2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$hashCode$1$Method = methods[0];
        //...初始化其它函数
    }

    //与原生java代理不同,针对委托类中的每个方法,在代理类中有两个实现,一个实现直接调用委托类方法,而另外一个则通过代理的回调进行调用。
    final void CGLIB$queryName$0() {
        super.queryName();
    }

    public final void queryName() {
        MethodInterceptor cglib$CALLBACK_2;
        MethodInterceptor cglib$CALLBACK_0;
        if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
            CGLIB$BIND_CALLBACKS(this);
            cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
        }
        if (cglib$CALLBACK_0 != null) {
            cglib$CALLBACK_2.intercept((Object)this, UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$queryName$0$Method, UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$emptyArgs, UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$queryName$0$Proxy);
            return;
        }
        super.queryName();
    }

    //...clone, toString, equals, finalize方法

    public static MethodProxy CGLIB$findMethodProxy(final Signature signature) {
        final String string = signature.toString();
        switch (string.hashCode()) {
            case -1574182249: {
                if (string.equals("finalize()V")) {
                    return UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$finalize$5$Proxy;
                }
                break;
            }
            //...
    }

    public UserDaoImpl$$EnhancerByCGLIB$$c1115585() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(final Callback[] value) {
        UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$THREAD_CALLBACKS.set(value);
    }

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

    private static final void CGLIB$BIND_CALLBACKS(final Object o) {
        final UserDaoImpl$$EnhancerByCGLIB$$c1115585 userDaoImpl$$EnhancerByCGLIB$$c1115585 = (UserDaoImpl$$EnhancerByCGLIB$$c1115585)o;
        if (!userDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$BOUND) {
            userDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$BOUND = true;
            Object o2;
            if ((o2 = UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$STATIC_CALLBACKS) != null) {
                userDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
            }
        }
    }

   //省略newInstance函数



    public Callback getCallback(final int n) {
        CGLIB$BIND_CALLBACKS(this);
        Object cglib$CALLBACK_0 = null;
        switch (n) {
            case 0: {
                cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0;
                break;
            }
            default: {
                cglib$CALLBACK_0 = null;
                break;
            }
        }
        return (Callback)cglib$CALLBACK_0;
    }

    public void setCallback(final int n, final Callback callback) {
        switch (n) {
            case 0: {
                this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
                break;
            }
        }
    }

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

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

    static {
        CGLIB$STATICHOOK1();
    }
}

从上面可以看到,Cglib生成的代理对象,相比于Java原生的代理对象,会生成更多的函数以及更多的静态变量,因此,在生成代理对象的时候,可想而知Cglib会耗时更多。

看完了Enhancer生成的代理类,那么剩下的两个FastClass的类,又有什么作用呢?

在介绍这两个类之前,我们先回顾一下前面提到的MethodInterceptor。

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        return proxy.invokeSuper(obj, args);
    }

再深究一下,methody proxy的invokerSuperinvoke方法

public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (IllegalArgumentException e) {
            if (fastClassInfo.i1 < 0)
                throw new IllegalArgumentException("Protected method: " + sig1);
            throw e;
        }
    }

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

不难发现,两者都引用了FastClassInfo。而实际上,两个FastClass正是因此而产生的。如果只生成代理对象,不通过代理对象调用方法的话,只会生成一个class文件,而一旦通过代理对象调用方法,就会生成三个class文件。

两个FastClass文件,是为了减少反射调用的时间,将反射调用转化为直接调用。
UserDaoImpl$$FastClassByCGLIB$$e8aae1d6.class

package com.study.cglib.learning;

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

public class UserDaoImpl$$FastClassByCGLIB$$e8aae1d6 extends FastClass
{
    public UserDaoImpl$$FastClassByCGLIB$$e8aae1d6(final Class clazz) {
        super(clazz);
    }

    public int getIndex(final Signature signature) {
        final String string = signature.toString();
        switch (string.hashCode()) {
            case -1725733088: {
                if (string.equals("getClass()Ljava/lang/Class;")) {
                    return 1;
                }
                break;
            }
            //其它函数的处理分支
        }
        return -1;
    }

    public int getIndex(final String s, final Class[] array) {
        Label_0438: {
            switch (s.hashCode()) {
                case -1807064205: {
                    if (!s.equals("queryName")) {
                        break;
                    }
                    switch (array.length) {
                        case 0: {
                            return 0;
                        }
                        default: {
                            break Label_0438;
                        }
                    }
                    break;
                }
                //其它函数的处理分支
            }
        }
        return -1;
    }

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

    public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
        final UserDaoImpl userDaoImpl = (UserDaoImpl)o;
        try {
            switch (n) {
                case 0: {
                    userDaoImpl.queryName();
                    return null;
                }
                //其它函数的处理分支
            }
        }
        catch (Throwable target) {
            throw new InvocationTargetException(target);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

    public Object newInstance(final int n, final Object[] array) throws InvocationTargetException {
        try {
            switch (n) {
                case 0: {
                    return new UserDaoImpl();
                }
            }
        }
        catch (Throwable target) {
            throw new InvocationTargetException(target);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

    public int getMaxIndex() {
        return 9;
    }
}

UserDaoImpl$$FastClassByCGLIB$$e8aae1d6实际上是上面invoke方法中使用的fastclass。而另外一个UserDaoImpl$$EnhancerByCGLIB$$c1115585$$FastClassByCGLIB$$d2f68b9b则是在invokeSuper中使用的fastclass。fastclass将反射调用转化成了直接调用。

小结

以上,就是Cglib中较为简单的部分,我们在这篇文章里面完成了Cglib生成动态代理的简单示例,为代理设置了回调类以及过滤类,并且分析了Cglib在生成动态代理、调用方法时会生成的两种不同类型的文件。
我们可以简略得出以下结论
- Cglib代理的方式是直接继承委托类,因而无法代理final类
- 在生成代理类的过程中,会生成两个方法,一个方法直接调用委托类中的对应方法,另一个方法则覆盖了委托类的对应方法,由此可知,Cglib生成类的速度要慢于Java原生代理,而且final方法也无法被代理。
- 除了生成代理类,Cglib还会生成相应的FastClass作为方法调用的索引,以减少反射调用所消耗的时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值