1. 程式人生 > >Cglib動態代理實現方式

Cglib動態代理實現方式

alt ber adl reat sge get 一次 復雜 super

Cglib動態代理實現方式

我們先通過一個demo看一下Cglib是如何實現動態代理的。

首先定義個服務類,有兩個方法並且其中一個方法用final來修飾。

技術分享圖片
public class PersonService {
    public PersonService() {
        System.out.println("PersonService構造");
    }
    //該方法不能被子類覆蓋
    final public Person getPerson(String code) {
        System.out.println("PersonService:getPerson>>"+code);
        return null;
    }

    public void setPerson() {
        System.out.println("PersonService:setPerson");
    }
}
技術分享圖片

Cglib是無法代理final修飾的方法的,具體原因我們一會通過源碼來分析。

然後,定義一個自定義MethodInterceptor。

技術分享圖片
public class CglibProxyIntercepter implements MethodInterceptor {
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("執行前...");
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("執行後...");
        return object;
    }
}
技術分享圖片

我們看一下intercept方法入參,sub:cglib生成的代理對象,method:被代理對象方法,objects:方法入參,methodProxy:代理方法

最後,我們寫個例子調用一下,並將Cglib生成的代理類class文件輸出磁盤方便我們反編譯查看源碼。

技術分享圖片
public class Test {
    public static void main(String[] args) {
//代理類class文件存入本地磁盤 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PersonService.class); enhancer.setCallback(new CglibProxyIntercepter()); PersonService proxy= (PersonService) enhancer.create();
proxy.setPerson();
proxy.getPerson("1");
} }
技術分享圖片

我們執行一下會發現getPerson因為加final修飾並沒有被代理,下面我們通過源碼分析一下。

執行前...
PersonService:setPerson
執行後...
PersonService:getPerson>>1

生成代理類

執行Test測試類可以得到Cglib生成的class文件,一共有三個class文件我們反編譯以後逐個說一下他們的作用。

技術分享圖片

PersonService$$EnhancerByCGLIB$$eaaaed75就是cglib生成的代理類,它繼承了PersonService類。
技術分享圖片
public class PersonService$$EnhancerByCGLIB$$eaaaed75
  extends PersonService
  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$setPerson$0$Method;//被代理方法
  private static final MethodProxy CGLIB$setPerson$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()
  {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class localClass1 = Class.forName("com.demo.proxy.cglib.PersonService$$EnhancerByCGLIB$$eaaaed75");//代理類
    Class localClass2;//被代理類PersionService
    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[] { "setPerson", "()V" }, (localClass2 = Class.forName("com.demo.proxy.cglib.PersonService")).getDeclaredMethods());
    CGLIB$setPerson$0$Method = tmp223_220[0];
    CGLIB$setPerson$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "setPerson", "CGLIB$setPerson$0");
    tmp223_220;
    return;
  }
  
技術分享圖片

我們通過代理類的源碼可以看到,代理類會獲得所有在父類繼承來的方法,並且會有MethodProxy與之對應,比如 Method CGLIB$setPerson$0$Method、MethodProxy CGLIB$setPerson$0$Proxy;

方法的調用

技術分享圖片
 
//代理方法(methodProxy.invokeSuper會調用)
final void CGLIB$setPerson$0() { super.setPerson(); } //被代理方法(methodProxy.invoke會調用,這就是為什麽在攔截器中調用methodProxy.invoke會死循環,一直在調用攔截器) public final void setPerson() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) {
//調用攔截器 var10000.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy); } else { super.setPerson(); } }
技術分享圖片

調用過程:代理對象調用this.setPerson方法->調用攔截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理對象setPerson方法

MethodProxy

攔截器MethodInterceptor中就是由MethodProxy的invokeSuper方法調用代理方法的,MethodProxy非常關鍵,我們分析一下它具體做了什麽。

  • 創建MethodProxy
技術分享圖片
public class MethodProxy {
    private Signature sig1;
    private Signature sig2;
    private MethodProxy.CreateInfo createInfo;
    private final Object initLock = new Object();
    private volatile MethodProxy.FastClassInfo fastClassInfo;
    //c1:被代理對象Class
//c2:代理對象Class
//desc:入參類型
//name1:被代理方法名
//name2:代理方法名 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; }
private static class CreateInfo {
Class c1;
Class c2;
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad;

public CreateInfo(Class c1, Class c2) {
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if(fromEnhancer != null) {
this.namingPolicy = fromEnhancer.getNamingPolicy();
this.strategy = fromEnhancer.getStrategy();
this.attemptLoad = fromEnhancer.getAttemptLoad();
}

}
}
 
技術分享圖片
  • invokeSuper調用
技術分享圖片
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
private static class FastClassInfo {
FastClass f1;//被代理類FastClass
FastClass f2;//代理類FastClass
int i1; //被代理類的方法簽名(index)
int i2;//代理類的方法簽名

private FastClassInfo() {
}
}
 
技術分享圖片
上面代碼調用過程就是獲取到代理類對應的FastClass,並執行了代理方法。還記得之前生成三個class文件嗎?PersonService$$EnhancerByCGLIB$$eaaaed75$$FastClassByCGLIB$$355cb7ea.class就是代理類的FastClass,
PersonService$$FastClassByCGLIB$$a076b035.class就是被代理類的FastClass。

FastClass機制

 Cglib動態代理執行代理方法效率之所以比JDK的高是因為Cglib采用了FastClass機制,它的原理簡單來說就是:為代理類和被代理類各生成一個Class,這個Class會為代理類或被代理類的方法分配一個index(int類型)。
這個index當做一個入參,FastClass就可以直接定位要調用的方法直接進行調用,這樣省去了反射調用,所以調用效率比JDK動態代理通過反射調用高。下面我們反編譯一個FastClass看看:
技術分享圖片
 //根據方法簽名獲取index
public int getIndex(Signature var1) { String var10000 = var1.toString(); switch(var10000.hashCode()) { case -2077043409: if(var10000.equals("getPerson(Ljava/lang/String;)Lcom/demo/pojo/Person;")) { return 21; } break; case -2055565910: if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) { return 12; } break; case -1902447170: if(var10000.equals("setPerson()V")) { return 7; } break; //省略部分代碼.....
 
//根據index直接定位執行方法 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException { eaaaed75 var10000 = (eaaaed75)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.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]); case 4: return var10000.newInstance((Callback)var3[0]); case 5: return var10000.newInstance((Callback[])var3[0]); case 6: var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]); return null; case 7: var10000.setPerson(); return null; case 8: var10000.setCallbacks((Callback[])var3[0]); return null; case 9: return var10000.getCallback(((Number)var3[0]).intValue()); case 10: return var10000.getCallbacks(); case 11: eaaaed75.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]); return null; case 12: eaaaed75.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]); return null; case 13: return eaaaed75.CGLIB$findMethodProxy((Signature)var3[0]); case 14: return var10000.CGLIB$toString$3(); case 15: return new Boolean(var10000.CGLIB$equals$2(var3[0])); case 16: return var10000.CGLIB$clone$5(); case 17: return new Integer(var10000.CGLIB$hashCode$4()); case 18: var10000.CGLIB$finalize$1(); return null; case 19: var10000.CGLIB$setPerson$0(); return null; //省略部分代碼.... } catch (Throwable var4) { throw new InvocationTargetException(var4); } throw new IllegalArgumentException("Cannot find matching method/constructor"); }
技術分享圖片

FastClass並不是跟代理類一塊生成的,而是在第一次執行MethodProxy invoke/invokeSuper時生成的並放在了緩存中。

技術分享圖片
//MethodProxy invoke/invokeSuper都調用了init()
private void init() { if(this.fastClassInfo == null) { Object var1 = this.initLock; synchronized(this.initLock) { if(this.fastClassInfo == null) { MethodProxy.CreateInfo ci = this.createInfo; MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo(); fci.f1 = helper(ci, ci.c1);//如果緩存中就取出,沒有就生成新的FastClass fci.f2 = helper(ci, ci.c2); fci.i1 = fci.f1.getIndex(this.sig1);//獲取方法的index fci.i2 = fci.f2.getIndex(this.sig2); this.fastClassInfo = fci; this.createInfo = null; } } } }
技術分享圖片

至此,Cglib動態代理的原理我們就基本搞清楚了,代碼細節有興趣可以再研究下。
最後我們總結一下JDK動態代理和Gglib動態代理的區別:
1.JDK動態代理是實現了被代理對象的接口,Cglib是繼承了被代理對象。
2.JDK和Cglib都是在運行期生成字節碼,JDK是直接寫Class字節碼,Cglib使用ASM框架寫Class字節碼,Cglib代理實現更復雜,生成代理類比JDK效率低。
3.JDK調用代理方法,是通過反射機制調用,Cglib是通過FastClass機制直接調用方法,Cglib執行效率更高。

轉自:https://www.cnblogs.com/monkey0307/p/8328821.html

Cglib動態代理實現方式