cglib动态代理源码解析 超级详细

一、CGLIB动态代理实例

小A同学想要找女朋友,自己又不好意思,所以需要一个媒婆帮助他找对象,媒婆作为小A的代理,下面我们使用cglib动态代理,来用代码实现下:

xiaoA:

public class XiaoA {
	public void findLove(){
		System.out.println("我是小A,帮我找个女朋友");
	}
}

meipo:

public class Meipo implements MethodInterceptor{

	public Object getInstance(Class clazz) throws Exception{
                //输出class文件
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
		Enhancer enhancer = new Enhancer();
                //设置父类
		enhancer.setSuperclass(clazz);
		//设置回调
		enhancer.setCallback(this);
                //返回代理类对象
		return enhancer.create();
	}
	

@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("我是媒婆:");
		System.out.println("------------");
		proxy.invokeSuper(obj, args);
		System.out.println("------------");
		System.out.println("寻找中....找到啦");
		return null;
	}

}

测试类:

public class TestCglibProxy {
	public static void main(String[] args) {
		try {
			// 代理类class文件存入本地磁盘方便我们反编译查看源码  D:/$Proxy0.class
			XiaoA obj = (XiaoA)new Meipo().getInstance(XiaoA.class);
			System.out.println(obj.getClass());
			obj.findLove();
					} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

打印结果:

是不是很神奇,下面我们通过源码来深入分析下。

二、代理类对象

从上面的可以看出,代理类对象是由Enhancer创建的。Enhancer是CGLIB的字节码增强器,可以·很方便的对类进行扩展,如上面的为生成的类设置父类。

创建代理对象会经过三步:

1.生成代理类的二进制字节码文件。

2.加载二进制字节码文件到JVM,生成class对象。

3.反射获得实例构造方法,创建代理对象。

下面我们来看下代理类class文件反编译后的代码:

现在,我们将代理类的class文件反编译一下(截取部分代码):

public class 173894b8 extends XiaoA
        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$findLove$0$Method; //被代理的方法
private static final MethodProxy CGLIB$findLove$0$Proxy; //代理方法
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;

static void CGLIB$STATICHOOK1()
        {
        Class localClass2;  //被代理类
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class localClass1 =  Class.forName("com.bzy.proxy.cglib.XiaoA$$EnhancerByCGLIB$$173894b8"); //代理类
        Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = tmp95_92[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");
        Method[] tmp115_95 = tmp95_92;
        CGLIB$equals$2$Method = tmp115_95[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        Method[] tmp135_115 = tmp115_95;
        CGLIB$toString$3$Method = tmp135_115[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        Method[] tmp155_135 = tmp135_115;
        CGLIB$hashCode$4$Method = tmp155_135[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
        Method[] tmp175_155 = tmp155_135;
        CGLIB$clone$5$Method = tmp175_155[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        tmp175_155;
        Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "findLove", "()V" }, (localClass2 = Class.forName("com.bzy.proxy.cglib.XiaoA")).getDeclaredMethods());
        CGLIB$findLove$0$Method = tmp223_220[0];
        CGLIB$findLove$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "findLove", "CGLIB$findLove$0");
        tmp223_220;
        return;
        }

}

通过代理类源码可以看到,代理类会获得所有在父类继承类的方法,并且有MethodProxy与之对应,例如CGLIB$findLove$0$Method与CGLIB$findLove$0$Proxy

三、方法调用

//代理方法(methodProxy.invokeSuper会调用)
final void CGLIB$findLove$0()
        {
        super.findLove();
        }
 //被代理方法(methodProxy.invoke会调用),这就是为什么在拦截器中调用methodProxy.invoke会死循环,因为下面的代码中一直在调用intercept
public final void findLove()
        {
        //this.CGLIB$CALLBACK_0==null
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        //首先执行CGLIB$BIND_CALLBACKS
        if (tmp4_1 == null)
        {        
        CGLIB$BIND_CALLBACKS(this);
        tmp4_1 = this.CGLIB$CALLBACK_0;
        }
        //再执行下面
        if (this.CGLIB$CALLBACK_0!= null)
          //调用拦截器,this就是当前的代理类
         tmp4_1.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs,CGLIB$findLove$0$Proxy);
        }

至此,调用的过程为:代理对象调用this.findlove->调用拦截器(intercept)->执行拦截器中的前面代码->执行invokeSuper(会执行CGLIB$findLove$0方法)->调用父类findLove->执行拦截器后面代码

相信大家这里会有疑问,他是如何通过invokeSuper调用到CGLIB$findLove$0方法的呢?

首先在调用intercept时,将CGLIB$findLove$0$Proxy传入到了intercept,然后下面我们分析下如何通过CGLIB$findLove$0$Proxy.invokeSuper调用到CGLIB$findLove$0方法的。

四、MethodProxy

在拦截器中,通过调用MethodProxy的invokeSuper方法来调用代理方法,我们来分析下MethodProxy看下他到底做了些什么?

首先我们看下MethodProxy的创建:

下图是上面代理类class中findLove的代理方法的创建,参数从左到右依次是:

被代理对象,代理对象,入参类型,被代理方法名,代理方法名

我们再看下MethodProxy.create的源码与类中的属性:

    private Signature sig1;  //相当于实体类的作用
    private Signature sig2;
    private MethodProxy.CreateInfo createInfo; //也可以认为是实体类
    private final Object initLock = new Object();
    private volatile MethodProxy.FastClassInfo fastClassInfo; //没有被初始化
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);  //将被代理对象与方法入参传入
        proxy.sig2 = new Signature(name2, desc);  //将代理对象与方法入参传入
      proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);//将被代理对象与代理对象传入
        return proxy;
    }

这里我们不深究代码,这里其实就可以认为是创建了一个MethodProxy对象,将代理对象,代理方法,被代理对象,被代理方法都传进去了。

下面我们再来看下invokeSuper的源码:

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            //fastCLass.invoke(代理方法索引,代理对象,方法参数)
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

首先调用了init方法:

    private void init() {
        //首先会执行这个分支,因为create时并没有给他赋值
        if (this.fastClassInfo == null) {
            //拿到上面的final对象作为锁
            Object var1 = this.initLock;
            synchronized(this.initLock) {
                //true
                if (this.fastClassInfo == null) {
                    //this.createInfo包含了代理类与被代理类的信息
                    MethodProxy.CreateInfo ci = this.createInfo;
                    //创建新的FastClassInfo对象
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);  //获得被代理对象的FastClass,如果缓存中有就从缓存中取出,没有就生成新的fastclass
                    fci.f2 = helper(ci, ci.c2);  //获得代理对象的FastClass,如果缓存中有就从缓存中取出,没有就生成新的fastclass
                    fci.i1=fci.f1.getIndex(this.sig1); //获得被代理对象中被代理方法的索引
                    fci.i2 = fci.f2.getIndex(this.sig2);//获得代理对象中代理方法的索引
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }
    }
                //FastClassInfo 构造方法
            private static class FastClassInfo {
                    FastClass f1;
                    FastClass f2;
                    int i1;
                    int i2;

            private FastClassInfo() {
                    }
    }

五、FastClass机制

Cglib动态代理执行代理方法效率之所以比JDK高是因为Cglib采用了FastClass机制,他为代理类和被代理类各生成了一个class,这个class会为代理类与被代理类的方法分类index。这个index作为方法参数,FastClass可以直接定位到要调用的方法进行调用,这样省去了反射调用,所以效率比JDK动态代理快。

FastClass不是与代理类一起生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放入缓存。

这里就不贴代码展示了,因为fastClass反编译后的代码有点乱,并且好多错误。

我们来从头总结下MethodProxy调用流程:

首先main方法中调用getInstance调用Enhancer创建代理类class文件,并将代理类引用返回。

然后调用代理对象.findLove()

然后通过反编译代理类,查看findLove方法,发现在findLove中会调用拦截器方法

然后在拦截器方法中会通过MethodProxy调用invokeSuper方法

调用invokeSuper方法为什么能调用到代理类的CGLIB$findLove$0?

下面看下MethodProxy的流程

create:在代理类中create一个MethodProxy,将被代理对象与方法传入Signature1,将代理对象与方法传入Signature2,将代理类对象与被代理类对象传入 MethodProxy.CreateInfo,Signature1与MethodProxy.CreateInfo可以认为是实体类存储信息的。

调用methodProxy.invokeSuper了,调用invokeSuper时又会首先调用init方法。

init方法就是给在create方法中没有赋值的fastclassInfo赋值,将createInfo与Signature1与Signature2中的信息取出,转成fastclass,如果fastclass在缓存中有就从缓存中取,没有的话就生成新的fastclass

通过Signature1与Signature2获得方法的索引,存入i1与i2

通过fci.f2.invoke(fci.i2, obj, args)  fastclass反射调用代理对象的代理方法的索引,直接定位到代理方法。

代理方法中调用super.findLove()

猜你喜欢

转载自blog.csdn.net/qq_37113604/article/details/90141567
今日推荐