CGLIB动态代理原理分析

原理

采用字节码技术,动态生成代理类,并在代理类中做回调处理完成方法增强。

假设业务类User,我们现在要对其进行处理。CGLIB动态代理会生成User的代理类UserProxy,其中UserProxy继承自User。既然是继承,那么必然User的方法他也有,除非用final修饰。

因此,我们可以在UserProxy中对User的方法做处理,实现代理的作用。

实例

现在通过代码看下:
业务类User

public class User {

    public final void getId(){
        System.out.println("这是final方法");
    }
    public void getName(String id){
        System.out.println("User方法-获取名称返回:"+id);
    }
}

设置业务类的拦截器,用于后面的方法拦截,做个性化处理。

public class UserMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("拦截器拦截前:"+method.getName());
        Object object=proxy.invokeSuper(obj,args);  //invokeSuper而非invoke
        System.out.println("拦截器拦截后:"+method.getName());
        return object;
    }
}

调用测试:

public class MainTest {
    public static void main(String[] args) {
        //输出cglib动态代理产生的代理类
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/**/Documents/log/cglibProxyClass");
        
       //以下用于设置动态代理类的参数
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(User.class);
        enhancer.setCallback(new UserMethodInterceptor());
        User user=(User)enhancer.create();
        user.getName("a");
    }
}

打印结果:

拦截器拦截前:getName
User方法-获取名称返回:a
拦截器拦截后:getName

重点说明

1.代理类

通过下面代码,我们可以在该路径下生成代理类。

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/**/Documents/log/cglibProxyClass"); 

下面截取部分代码:

public class User$$EnhancerByCGLIB$$1e96607 extends User implements Factory {
	 final void CGLIB$getName$0(String var1) {
        super.getName(var1);
    }

    public final void getName(String var1) {
        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$getName$0$Method, new Object[]{var1}, CGLIB$getName$0$Proxy);
        } else {
            super.getName(var1);
        }
    }
}

其中:
a.代理类与被代理类的继承关系( extends)
b.final修饰的方法无法继承,生成的代理类中并没有getId()。
c.普通方法getName()有两个,一个是直接调用父类的方法,另一个先处理了拦截(intercept)。

2.CGLIB效率——FastClass机制

在JDK1.8之前,CGLIB动态代理的效率要比JDK动态代理高很多。这是为什么呢?如何实现的呢?
JDK动态代理使用的是反射,那么CGLIB是如何处理的呢?
CGLIB使用FastClass机制来实现对被拦截方法的调用。对类的方法建立索引,通过索引来调用相应的方法,快速高效。

public class ClassA {

    public void a1(){
        System.out.println("这是ClassA的a1方法");
    }

    public void a2(){
        System.out.println("这是ClassA的a2方法");
    }
}

public class ClassB {
    public int getIndex(String signature){
        switch (signature.hashCode()){
            case 3014151:
                return 1;
            case 3014521:
                return 2;
        }
        return -1;
    }

    public void invoke(int index,Object object,Object[] objects){
        ClassA classA=new ClassA();
        switch (index){
            case 1:
                classA.a1();
                return ;
            case 2:
                classA.a2();
                return ;
        }

    }
}

通过在一个类中建立另一个类的方法的索引,就可以快速准确的定位到调用的方法。无需使用反射。但也可以发现,这种方式弊端就是两个类过于耦合,维护代价很大。

对应到源码中的FastClassInfo类

private static class FastClassInfo
    {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;
    }

f1,f2分别对应代理类、被代理类,按照上面说的类方法索引的方式,生成新的类,方便使用。在例子中看一下,如下图所示。
CGLIB动态代理原理分析_第1张图片
被代理类的FastClass:

public class User$$FastClassByCGLIB$$8b45c2e9 extends FastClass {

	    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 2;
            }
            break;
        case 1871866270:
            if (var10000.equals("getName(Ljava/lang/String;)V")) {
                return 0;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 3;
            }
            break;
        case 1955814084:
            if (var10000.equals("getId()V")) {
                return 1;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 4;
            }
        }

        return -1;
    }
	
	 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
	   //实例化User对象,根据int值进行调用。
        User var10000 = (User)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.getName((String)var3[0]);
                return null;
            case 1:
                var10000.getId();
                return null;
            case 2:
                return new Boolean(var10000.equals(var3[0]));
            case 3:
                return var10000.toString();
            case 4:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

}

代理类的FastClass:
(类似部分略过)

public class User$$EnhancerByCGLIB$$1e96607$$FastClassByCGLIB$$6706c1cf extends FastClass {
	 public int getIndex(Signature var1) {
		//具体实现略
	}

	  public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
	   //“1e96607”即对应上面截图中的“User$$EnhancerByCGLIB$$1e96607”
        1e96607 var10000 = (1e96607)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                return new Boolean(var10000.equals(var3[0]));
            case 1:
                return var10000.toString();
            case 2:
                return new Integer(var10000.hashCode());
            case 3:
                return var10000.clone();
            case 4:
                var10000.getName((String)var3[0]);
                return null;
            case 5:
                return var10000.newInstance((Callback[])var3[0]);
            case 6:
                return var10000.newInstance((Callback)var3[0]);
            case 7:
                return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
            case 8:
                return 1e96607.CGLIB$findMethodProxy((Signature)var3[0]);
            case 9:
                var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                return null;
            case 10:
                var10000.setCallbacks((Callback[])var3[0]);
                return null;
            case 11:
                1e96607.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                return null;
            case 12:
                1e96607.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                return null;
            case 13:
                return var10000.getCallback(((Number)var3[0]).intValue());
            case 14:
                return var10000.getCallbacks();
            case 15:
                1e96607.CGLIB$STATICHOOK1();
                return null;
            case 16:
                var10000.CGLIB$getName$0((String)var3[0]);
                return null;
            case 17:
                return new Boolean(var10000.CGLIB$equals$1(var3[0]));
            case 18:
                return var10000.CGLIB$toString$2();
            case 19:
                return new Integer(var10000.CGLIB$hashCode$3());
            case 20:
                return var10000.CGLIB$clone$4();
            case 21:
                var10000.getId();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

}

3.字节码技术

字节码增强技术相当于是一把打开运行时JVM的钥匙,利用它可以动态地对运行中的程序做修改,也可以跟踪JVM运行中程序的状态。
上面我们介绍过,代理类是动态生成的,负责拦截调用的FastClass也是动态生成的。通过这些动态生成的类,我们实现了对原始业务类中方法的增强处理。

小结

1.反射是个好东西,但效率一般不高。在选型时,要综合考虑,平衡把握,没有恒强的技术。
2.系统间的低耦合、可维护、可插拔都是我们所追求的。实现方式多种多样,各个方面各个层次的实现思路也是不同的。

你可能感兴趣的:(工作)