最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java的Cglib動(dòng)態(tài)代理實(shí)現(xiàn)方式詳解

 更新時(shí)間:2023年11月27日 09:03:52   作者:ps酷教程  
這篇文章主要介紹了Java的Cglib動(dòng)態(tài)代理實(shí)現(xiàn)方式詳解,CGLIB是強(qiáng)大的、高性能的代碼生成庫,被廣泛應(yīng)用于AOP框架,它底層使用ASM來操作字節(jié)碼生成新的類,為對(duì)象引入間接級(jí)別,以控制對(duì)象的訪問,需要的朋友可以參考下

Cglib動(dòng)態(tài)代理

我們先通過一個(gè)demo看一下Cglib是如何實(shí)現(xiàn)動(dòng)態(tài)代理的。

首先定義個(gè)服務(wù)類,有兩個(gè)方法并且其中一個(gè)方法用final來修飾。

public class PersonService {
    public PersonService() {
        System.out.println("PersonService構(gòu)造");
    }
    //該方法不能被子類覆蓋
    final public Person getPerson(String code) {
        System.out.println("PersonService:getPerson>>"+code);
        return null;
    }
    public void setPerson() {
        System.out.println("PersonService:setPerson");
    }
}

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

然后,定義一個(gè)自定義MethodInterceptor。

public class CglibProxyIntercepter implements MethodInterceptor {
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("執(zhí)行前...");
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("執(zhí)行后...");
        return object;
    }
}

我們看一下intercept方法入?yún)?,sub:cglib生成的代理對(duì)象,method:被代理對(duì)象方法,objects:方法入?yún)?,methodProxy:代理方法

最后,我們寫個(gè)例子調(diào)用一下,并將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");     
    } 
}

我們執(zhí)行一下會(huì)發(fā)現(xiàn)getPerson因?yàn)榧觙inal修飾并沒有被代理,下面我們通過源碼分析一下。

執(zhí)行前...
PersonService:setPerson
執(zhí)行后...
PersonService:getPerson>>1

生成代理類 執(zhí)行Test測試類可以得到Cglib生成的class文件,一共有三個(gè)class文件我們反編譯以后逐個(gè)說一下他們的作用。

在這里插入圖片描述

 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;
  }

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

方法的調(diào)用

//代理方法(methodProxy.invokeSuper會(huì)調(diào)用)   
final void CGLIB$setPerson$0() {
   super.setPerson();
}
//被代理方法(methodProxy.invoke會(huì)調(diào)用,這就是為什么在攔截器中調(diào)用methodProxy.invoke會(huì)死循環(huán),一直在調(diào)用攔截器)
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) {         //調(diào)用攔截器
      var10000.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy);
   } else {
      super.setPerson();
   }
}

調(diào)用過程:代理對(duì)象調(diào)用this.setPerson方法->調(diào)用攔截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理對(duì)象setPerson方法

MethodProxy

攔截器MethodInterceptor中就是由MethodProxy的invokeSuper方法調(diào)用代理方法的,MethodProxy非常關(guān)鍵,我們分析一下它具體做了什么。

創(chuàng)建MethodProxy

package com.zzhua.cglib.callback;

import net.sf.cglib.core.AbstractClassGenerator;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.NamingPolicy;

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:被代理對(duì)象Class    
    //c2:代理對(duì)象Class    
    //desc:入?yún)㈩愋?   
    //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調(diào)用

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() {
    }
}

上面代碼調(diào)用過程就是獲取到代理類對(duì)應(yīng)的FastClass,并執(zhí)行了代理方法。還記得之前生成三個(gè)class文件嗎?

PersonService$$EnhancerByCGLIB$$eaaaed75$$FastClassByCGLIB$$355cb7ea.class就是代理類的FastClass,

PersonService$$FastClassByCGLIB$$a076b035.class就是被代理類的FastClass。

FastClass機(jī)制

Cglib動(dòng)態(tài)代理執(zhí)行代理方法效率之所以比JDK的高是因?yàn)镃glib采用了FastClass機(jī)制,它的原理簡單來說就是:為代理類和被代理類各生成一個(gè)Class,這個(gè)Class會(huì)為代理類或被代理類的方法分配一個(gè)index(int類型)。

這個(gè)index當(dāng)做一個(gè)入?yún)?,F(xiàn)astClass就可以直接定位要調(diào)用的方法直接進(jìn)行調(diào)用,這樣省去了反射調(diào)用,所以調(diào)用效率比JDK動(dòng)態(tài)代理通過反射調(diào)用高。

下面我們反編譯一個(gè)FastClass看看:

//根據(jù)方法簽名獲取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;
  //省略部分代碼.....  

//根據(jù)index直接定位執(zhí)行方法
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并不是跟代理類一塊生成的,而是在第一次執(zhí)行MethodProxy invoke/invokeSuper時(shí)生成的并放在了緩存中。

//MethodProxy invoke/invokeSuper都調(diào)用了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動(dòng)態(tài)代理的原理我們就基本搞清楚了,代碼細(xì)節(jié)有興趣可以再研究下。

最后我們總結(jié)一下JDK動(dòng)態(tài)代理和Gglib動(dòng)態(tài)代理的區(qū)別:

1.JDK動(dòng)態(tài)代理是實(shí)現(xiàn)了被代理對(duì)象的接口,Cglib是繼承了被代理對(duì)象。

2.JDK和Cglib都是在運(yùn)行期生成字節(jié)碼,JDK是直接寫Class字節(jié)碼,Cglib使用ASM框架寫Class字節(jié)碼,Cglib代理實(shí)現(xiàn)更復(fù)雜,生成代理類比JDK效率低。

3.JDK調(diào)用代理方法,是通過反射機(jī)制調(diào)用,Cglib是通過FastClass機(jī)制直接調(diào)用方法,Cglib執(zhí)行效率更高。

到此這篇關(guān)于Java的Cglib動(dòng)態(tài)代理實(shí)現(xiàn)方式詳解的文章就介紹到這了,更多相關(guān)Cglib動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot自動(dòng)裝配原理小結(jié)

    SpringBoot自動(dòng)裝配原理小結(jié)

    Spring Boot主要作用就是簡化Spring應(yīng)用的開發(fā),開發(fā)者只需要通過少量代碼就可以創(chuàng)建一個(gè)Spring應(yīng)用,而達(dá)到這一目的最核心的思想就是約定優(yōu)于配置。
    2021-05-05
  • Mybatis Plus使用@TableId的示例詳解

    Mybatis Plus使用@TableId的示例詳解

    在 MyBatis Plus 中,@TableId 注解是用于標(biāo)記實(shí)體類中的主鍵字段,它可以更方便地處理主鍵相關(guān)的操作,如自動(dòng)填充主鍵值或識(shí)別主鍵字段,這篇文章主要介紹了Mybatis Plus使用@TableId,需要的朋友可以參考下
    2024-08-08
  • 踩坑批量更新sql報(bào)錯(cuò),實(shí)際sql能夠正常執(zhí)行的問題

    踩坑批量更新sql報(bào)錯(cuò),實(shí)際sql能夠正常執(zhí)行的問題

    在項(xiàng)目工程遷移過程中,遇到了一個(gè)批量更新接口在新工程中報(bào)錯(cuò)的問題,通過分析,排除了代碼錯(cuò)誤的可能,最終發(fā)現(xiàn)是由于數(shù)據(jù)庫連接配置不當(dāng)導(dǎo)致的,在jdbc連接字符串中加入allowMultiQueries=true參數(shù)后,問題得以解決,這個(gè)參數(shù)的作用是允許SQL批量執(zhí)行
    2022-12-12
  • Java?API文檔的使用方法詳解

    Java?API文檔的使用方法詳解

    在開發(fā)過程中如果遇到疑難問題,除了可以在網(wǎng)絡(luò)中尋找答案,也可以在Java API幫助文檔(簡稱"JDK文檔"”)中查找答案,下面這篇文章主要給大家介紹了關(guān)于Java?API文檔使用的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • SpringCloud?中防止繞過網(wǎng)關(guān)請(qǐng)求直接訪問后端服務(wù)的解決方法

    SpringCloud?中防止繞過網(wǎng)關(guān)請(qǐng)求直接訪問后端服務(wù)的解決方法

    這篇文章主要介紹了SpringCloud中如何防止繞過網(wǎng)關(guān)請(qǐng)求直接訪問后端服務(wù),本文給大家分享三種解決方案,需要的朋友可以參考下
    2023-06-06
  • JDK  keytool證書工具功能代碼解析

    JDK keytool證書工具功能代碼解析

    這篇文章主要介紹了JDK keytool證書工具功能代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • Springboot處理異常的常見方式

    Springboot處理異常的常見方式

    SpringBoot框架異常處理有多種處理方式,今天就帶大家了解一下常見的springboot異常處理方式,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • (starters)springboot-starter整合阿里云datahub方式

    (starters)springboot-starter整合阿里云datahub方式

    這篇文章主要介紹了(starters)springboot-starter整合阿里云datahub方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java 解決讀寫本地文件中文亂碼的問題

    Java 解決讀寫本地文件中文亂碼的問題

    這篇文章主要介紹了Java 解決讀寫本地文件中文亂碼的問題的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • Retrofit+RxJava實(shí)現(xiàn)帶進(jìn)度下載文件

    Retrofit+RxJava實(shí)現(xiàn)帶進(jìn)度下載文件

    這篇文章主要為大家詳細(xì)介紹了Retrofit+RxJava實(shí)現(xiàn)帶進(jìn)度下載文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05

最新評(píng)論

仁怀市| 鹤壁市| 西乡县| 张家界市| 云南省| 襄汾县| 蚌埠市| 徐闻县| 永仁县| 睢宁县| 浦北县| 天柱县| 类乌齐县| 静乐县| 高淳县| 潜山县| 清水县| 阿克苏市| 朔州市| 巨鹿县| 长治县| 新绛县| 阜城县| 舟曲县| 贵阳市| 民权县| 长汀县| 哈巴河县| 旅游| 凯里市| 永福县| 丰都县| 慈溪市| 汶川县| 调兵山市| 新源县| 上犹县| 龙胜| 武夷山市| 游戏| 襄樊市|