国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java cglib動(dòng)態(tài)代理原理分析

發(fā)布時(shí)間:2021-07-06 11:13 來(lái)源:腳本之家 閱讀:0 作者:cruze_lee 欄目: 開(kāi)發(fā)技術(shù)

目錄

本文分下面三個(gè)部分來(lái)分析cglib動(dòng)態(tài)代理的原理。

  1. cglib 動(dòng)態(tài)代理示例
  2. 代理類(lèi)分析
  3. Fastclass 機制分析

一、cglib 動(dòng)態(tài)代理示例  

public class Target{
    public void f(){
        System.out.println("Target f()");
    }
    public void g(){
        System.out.println("Target g()");
    }
}

public class Interceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args,    MethodProxy proxy) throws Throwable {
        System.out.println("I am intercept begin");
//Note: 此處一定要使用proxy的invokeSuper方法來(lái)調用目標類(lèi)的方法
        proxy.invokeSuper(obj, args);
        System.out.println("I am intercept end");
        return null;
    }
}

public class Test {
    public static void main(String[] args) {
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code");
         //實(shí)例化一個(gè)增強器,也就是cglib中的一個(gè)class generator
        Enhancer eh = new Enhancer();
         //設置目標類(lèi)
        eh.setSuperclass(Target.class);
        // 設置攔截對象
        eh.setCallback(new Interceptor());
        // 生成代理類(lèi)并返回一個(gè)實(shí)例
        Target t = (Target) eh.create();
        t.f();
        t.g();
    }
}

運行結果為:

I am intercept begin
Target f()
I am intercept end
I am intercept begin
Target g()
I am intercept end 

與JDK動(dòng)態(tài)代理相比,cglib可以實(shí)現對一般類(lèi)的代理而無(wú)需實(shí)現接口。在上例中通過(guò)下列步驟來(lái)生成目標類(lèi)Target的代理類(lèi):

  1. 創(chuàng )建Enhancer實(shí)例
  2. 通過(guò)setSuperclass方法來(lái)設置目標類(lèi)
  3. 通過(guò)setCallback 方法來(lái)設置攔截對象
  4. create方法生成Target的代理類(lèi),并返回代理類(lèi)的實(shí)例

二、代理類(lèi)分析

      在示例代碼中我們通過(guò)設置DebuggingClassWriter.DEBUG_LOCATION_PROPERTY的屬性值來(lái)獲取cglib生成的代理類(lèi)。通過(guò)之前分析的命名規則我們可以很容易的在F:\\code下面找到生成的代理類(lèi) Target$$EnhancerByCGLIB$$788444a0.class 。

使用jd-gui進(jìn)行反編譯(由于版本的問(wèn)題,此處只能顯示部分代碼,可以結合javap的反編譯結果來(lái)進(jìn)行分析),由于cglib會(huì )代理Object中的finalize,equals, toString,hashCode,clone方法,為了清晰的展示代理類(lèi)我們省略這部分代碼,反編譯的結果如下:

public class Target$$EnhancerByCGLIB$$788444a0 extends Target 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$g$0$Method;
	private static final MethodProxy CGLIB$g$0$Proxy;
	private static final Object[] CGLIB$emptyArgs;
	private static final Method CGLIB$f$1$Method;
	private static final MethodProxy CGLIB$f$1$Proxy;
	static void CGLIB$STATICHOOK1()
	{
		CGLIB$THREAD_CALLBACKS = new ThreadLocal();
		CGLIB$emptyArgs = new Object[0];
		Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0");
		Class localClass2;
		Method[] tmp60_57 = ReflectUtils.findMethods(new String[] { "g", "()V", "f", "()V" }, (localClass2 = Class.forName("net.sf.cglib.test.Target")).getDeclaredMethods());
		CGLIB$g$0$Method = tmp60_57[0];
		CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");
		CGLIB$f$1$Method = tmp60_57[1];
		CGLIB$f$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "f", "CGLIB$f$1");
	}
	final void CGLIB$g$0()
	{
		super.g();
	}
	public final void g()
	{
		MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
		if (tmp4_1 == null)
		{
			CGLIB$BIND_CALLBACKS(this);
			tmp4_1 = this.CGLIB$CALLBACK_0;
		}
		if (this.CGLIB$CALLBACK_0 != null) {
			tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy);
		} else{
			super.g();
		}
	}
}

      代理類(lèi)(Target$$EnhancerByCGLIB$$788444a0)繼承了目標類(lèi)(Target),至于代理類(lèi)實(shí)現的factory接口與本文無(wú)關(guān),殘忍無(wú)視。代理類(lèi)為每個(gè)目標類(lèi)的方法生成兩個(gè)方法,例如針對目標類(lèi)中的每個(gè)非private方法,代理類(lèi)會(huì )生成兩個(gè)方法,以g方法為例:一個(gè)是@Override的g方法,一個(gè)是CGLIB$g$0(CGLIB$g$0相當于目標類(lèi)的g方法)。我們在示例代碼中調用目標類(lèi)的方法t.g()時(shí),實(shí)際上調用的是代理類(lèi)中的g()方法。接下來(lái)我們著(zhù)重分析代理類(lèi)中的g方法,看看是怎么實(shí)現的代理功能。

      當調用代理類(lèi)的g方法時(shí),先判斷是否已經(jīng)存在實(shí)現了MethodInterceptor接口的攔截對象,如果沒(méi)有的話(huà)就調用CGLIB$BIND_CALLBACKS方法來(lái)獲取攔截對象,CGLIB$BIND_CALLBACKS的反編譯結果如下:

private static final void CGLIB$BIND_CALLBACKS(java.lang.Object);
  Code:
   0:   aload_0
   1:   checkcast       #2; //class net/sf/cglib/test/Target$$EnhancerByCGLIB$$788444a0
   4:   astore_1
   5:   aload_1
   6:   getfield        #212; //Field CGLIB$BOUND:Z
   9:   ifne    52
   12:  aload_1
   13:  iconst_1
   14:  putfield        #212; //Field CGLIB$BOUND:Z
   17:  getstatic       #24; //Field CGLIB$THREAD_CALLBACKS:Ljava/lang/ThreadLocal;
   20:  invokevirtual   #215; //Method java/lang/ThreadLocal.get:()Ljava/lang/Object;
   23:  dup
   24:  ifnonnull       39
   27:  pop
   28:  getstatic       #210; //Field CGLIB$STATIC_CALLBACKS:[Lnet/sf/cglib/proxy/Callback;
   31:  dup
   32:  ifnonnull       39
   35:  pop
   36:  goto    52
   39:  checkcast       #216; //class "[Lnet/sf/cglib/proxy/Callback;"
   42:  aload_1
   43:  swap
   44:  iconst_0
   45:  aaload
   46:  checkcast       #48; //class net/sf/cglib/proxy/MethodInterceptor
   49:  putfield        #36; //Field CGLIB$CALLBACK_0:Lnet/sf/cglib/proxy/MethodInterceptor;
   52:  return

為了方便閱讀,等價(jià)的代碼如下:

private static final void CGLIB$BIND_CALLBACKS(Object o){
        Target$$EnhancerByCGLIB$$788444a0 temp_1 = (Target$$EnhancerByCGLIB$$788444a0)o;
        Object temp_2;
        Callback[] temp_3
        if(temp_1.CGLIB$BOUND == true){
            return;
        }
        temp_1.CGLIB$BOUND = true;
        temp_2 = CGLIB$THREAD_CALLBACKS.get();
        if(temp_2!=null){
            temp_3 = (Callback[])temp_2;
        }
        else if(CGLIB$STATIC_CALLBACKS!=null){
            temp_3 = CGLIB$STATIC_CALLBACKS;
        }
        else{
            return;
        }
        temp_1.CGLIB$CALLBACK_0 = (MethodInterceptor)temp_3[0];
        return;
    }

CGLIB$BIND_CALLBACKS 先從CGLIB$THREAD_CALLBACKS中g(shù)et攔截對象,如果獲取不到的話(huà),再從CGLIB$STATIC_CALLBACKS來(lái)獲取,如果也沒(méi)有則認為該方法不需要代理。

那么攔截對象是如何設置到CGLIB$THREAD_CALLBACKS 或者 CGLIB$STATIC_CALLBACKS中的呢?

在Jdk動(dòng)態(tài)代理中攔截對象是在實(shí)例化代理類(lèi)時(shí)由構造函數傳入的,在cglib中是調用Enhancer的firstInstance方法來(lái)生成代理類(lèi)實(shí)例并設置攔截對象的。firstInstance的調用軌跡為:

  1. Enhancer:firstInstance
  2. Enhancer:createUsingReflection
  3. Enhancer:setThreadCallbacks
  4. Enhancer:setCallbacksHelper
  5. Target$$EnhancerByCGLIB$$788444a0 : CGLIB$SET_THREAD_CALLBACKS

 在第5步,調用了代理類(lèi)的CGLIB$SET_THREAD_CALLBACKS來(lái)完成攔截對象的注入。下面我們看一下CGLIB$SET_THREAD_CALLBACKS的反編譯結果:

public static void CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]);
  Code:
   0:   getstatic       #24; //Field CGLIB$THREAD_CALLBACKS:Ljava/lang/ThreadLocal;
   3:   aload_0
   4:   invokevirtual   #207; //Method java/lang/ThreadLocal.set:(Ljava/lang/Object;)V
   7:   return

在CGLIB$SET_THREAD_CALLBACKS方法中調用了CGLIB$THREAD_CALLBACKS的set方法來(lái)保存攔截對象,在CGLIB$BIND_CALLBACKS方法中使用了CGLIB$THREAD_CALLBACKS的get方法來(lái)獲取攔截對象,并保存到CGLIB$CALLBACK_0中。這樣,在我們調用代理類(lèi)的g方法時(shí),就可以獲取到我們設置的攔截對象,然后通過(guò)  tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy)  來(lái)實(shí)現代理。這里來(lái)解釋一下intercept方法的參數含義:

@para1 obj :代理對象本身

@para2 method : 被攔截的方法對象

@para3 args:方法調用入參

@para4 proxy:用于調用被攔截方法的方法代理對象

這里會(huì )有一個(gè)疑問(wèn),為什么不直接反射調用代理類(lèi)生成的(CGLIB$g$0)來(lái)間接調用目標類(lèi)的被攔截方法,而使用proxy的invokeSuper方法呢?這里就涉及到了另外一個(gè)點(diǎn)— FastClass 。

三、Fastclass 機制分析

     Jdk動(dòng)態(tài)代理的攔截對象是通過(guò)反射的機制來(lái)調用被攔截方法的,反射的效率比較低,所以cglib采用了FastClass的機制來(lái)實(shí)現對被攔截方法的調用。FastClass機制就是對一個(gè)類(lèi)的方法建立索引,通過(guò)索引來(lái)直接調用相應的方法,下面用一個(gè)小例子來(lái)說(shuō)明一下,這樣比較直觀(guān)

public class test10 {
    public static void main(String[] args){
        Test tt = new Test();
        Test2 fc = new Test2();
        int index = fc.getIndex("f()V");
        fc.invoke(index, tt, null);
    }
}

class Test{
    public void f(){
        System.out.println("f method");
    }
    
    public void g(){
        System.out.println("g method");
    }
}
class Test2{
    public Object invoke(int index, Object o, Object[] ol){
        Test t = (Test) o;
        switch(index){
        case 1:
            t.f();
            return null;
        case 2:
            t.g();
            return null;
        }
        return null;
    }
    
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1;
    }
}

上例中,Test2是Test的Fastclass,在Test2中有兩個(gè)方法getIndex和invoke。在getIndex方法中對Test的每個(gè)方法建立索引,并根據入參(方法名+方法的描述符)來(lái)返回相應的索引。Invoke根據指定的索引,以ol為入參調用對象O的方法。這樣就避免了反射調用,提高了效率。代理類(lèi)(Target$$EnhancerByCGLIB$$788444a0)中與生成Fastclass相關(guān)的代碼如下:

Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0");
localClass2 = Class.forName("net.sf.cglib.test.Target");
CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");

MethodProxy中會(huì )對localClass1和localClass2進(jìn)行分析并生成FastClass,然后再使用getIndex來(lái)獲取方法g 和 CGLIB$g$0的索引,具體的生成過(guò)程將在后續進(jìn)行介紹,這里介紹一個(gè)關(guān)鍵的內部類(lèi):

private static class FastClassInfo
    {
        FastClass f1; // net.sf.cglib.test.Target的fastclass
        FastClass f2; // Target$$EnhancerByCGLIB$$788444a0 的fastclass
        int i1; //方法g在f1中的索引
        int i2; //方法CGLIB$g$0在f2中的索引
    }

MethodProxy 中invokeSuper方法的代碼如下:

    FastClassInfo fci = fastClassInfo;
    return fci.f2.invoke(fci.i2, obj, args);

當調用invokeSuper方法時(shí),實(shí)際上是調用代理類(lèi)的CGLIB$g$0方法,CGLIB$g$0直接調用了目標類(lèi)的g方法。所以,在第一節示例代碼中我們使用invokeSuper方法來(lái)調用被攔截的目標類(lèi)方法。

至此,我們已經(jīng)了解cglib動(dòng)態(tài)代理的工作原理,接下來(lái)會(huì )對cglib的相關(guān)源碼進(jìn)行分析。

以上就是Java cglib動(dòng)態(tài)代理原理分析的詳細內容,更多關(guān)于Java cglib動(dòng)態(tài)代理原理的資料請關(guān)注腳本之家其它相關(guān)文章!

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

东京热一区二区三区无码视频| A级毛片100部免费观看| 四虎永久在线精品免费播放| 在线 | 国产精品99传媒丿| 久久丫线这里只精品| 国产情侣一区二区|