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

JDK動(dòng)態(tài)代理之ProxyGenerator生成代理類(lèi)的字節(jié)碼文件解析

 更新時(shí)間:2018年02月27日 14:57:50   作者:勞夫子  
這篇文章主要為大家詳細(xì)介紹了JDK動(dòng)態(tài)代理之ProxyGenerator生成代理類(lèi)的字節(jié)碼文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

通過(guò)前面幾篇的分析,我們知道代理類(lèi)是通過(guò)Proxy類(lèi)的ProxyClassFactory工廠生成的,這個(gè)工廠類(lèi)會(huì)去調(diào)用ProxyGenerator類(lèi)的generateProxyClass()方法來(lái)生成代理類(lèi)的字節(jié)碼。ProxyGenerator這個(gè)類(lèi)存放在sun.misc包下,我們可以通過(guò)OpenJDK源碼來(lái)找到這個(gè)類(lèi),該類(lèi)的generateProxyClass()靜態(tài)方法的核心內(nèi)容就是去調(diào)用generateClassFile()實(shí)例方法來(lái)生成Class文件。我們直接來(lái)看generateClassFile()這個(gè)方法內(nèi)部做了些什么。

private byte[] generateClassFile() {
  //第一步, 將所有的方法組裝成ProxyMethod對(duì)象
  //首先為代理類(lèi)生成toString, hashCode, equals等代理方法
  addProxyMethod(hashCodeMethod, Object.class);
  addProxyMethod(equalsMethod, Object.class);
  addProxyMethod(toStringMethod, Object.class);
  //遍歷每一個(gè)接口的每一個(gè)方法, 并且為其生成ProxyMethod對(duì)象
  for (int i = 0; i < interfaces.length; i++) {
    Method[] methods = interfaces[i].getMethods();
    for (int j = 0; j < methods.length; j++) {
      addProxyMethod(methods[j], interfaces[i]);
    }
  }
  //對(duì)于具有相同簽名的代理方法, 檢驗(yàn)方法的返回值是否兼容
  for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
    checkReturnTypes(sigmethods);
  }
  
  //第二步, 組裝要生成的class文件的所有的字段信息和方法信息
  try {
    //添加構(gòu)造器方法
    methods.add(generateConstructor());
    //遍歷緩存中的代理方法
    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
      for (ProxyMethod pm : sigmethods) {
        //添加代理類(lèi)的靜態(tài)字段, 例如:private static Method m1;
        fields.add(new FieldInfo(pm.methodFieldName,
            "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
        //添加代理類(lèi)的代理方法
        methods.add(pm.generateMethod());
      }
    }
    //添加代理類(lèi)的靜態(tài)字段初始化方法
    methods.add(generateStaticInitializer());
  } catch (IOException e) {
    throw new InternalError("unexpected I/O Exception");
  }
  
  //驗(yàn)證方法和字段集合不能大于65535
  if (methods.size() > 65535) {
    throw new IllegalArgumentException("method limit exceeded");
  }
  if (fields.size() > 65535) {
    throw new IllegalArgumentException("field limit exceeded");
  }

  //第三步, 寫(xiě)入最終的class文件
  //驗(yàn)證常量池中存在代理類(lèi)的全限定名
  cp.getClass(dotToSlash(className));
  //驗(yàn)證常量池中存在代理類(lèi)父類(lèi)的全限定名, 父類(lèi)名為:"java/lang/reflect/Proxy"
  cp.getClass(superclassName);
  //驗(yàn)證常量池存在代理類(lèi)接口的全限定名
  for (int i = 0; i < interfaces.length; i++) {
    cp.getClass(dotToSlash(interfaces[i].getName()));
  }
  //接下來(lái)要開(kāi)始寫(xiě)入文件了,設(shè)置常量池只讀
  cp.setReadOnly();
  
  ByteArrayOutputStream bout = new ByteArrayOutputStream();
  DataOutputStream dout = new DataOutputStream(bout);
  try {
    //1.寫(xiě)入魔數(shù)
    dout.writeInt(0xCAFEBABE);
    //2.寫(xiě)入次版本號(hào)
    dout.writeShort(CLASSFILE_MINOR_VERSION);
    //3.寫(xiě)入主版本號(hào)
    dout.writeShort(CLASSFILE_MAJOR_VERSION);
    //4.寫(xiě)入常量池
    cp.write(dout);
    //5.寫(xiě)入訪問(wèn)修飾符
    dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
    //6.寫(xiě)入類(lèi)索引
    dout.writeShort(cp.getClass(dotToSlash(className)));
    //7.寫(xiě)入父類(lèi)索引, 生成的代理類(lèi)都繼承自Proxy
    dout.writeShort(cp.getClass(superclassName));
    //8.寫(xiě)入接口計(jì)數(shù)值
    dout.writeShort(interfaces.length);
    //9.寫(xiě)入接口集合
    for (int i = 0; i < interfaces.length; i++) {
      dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
    }
    //10.寫(xiě)入字段計(jì)數(shù)值
    dout.writeShort(fields.size());
    //11.寫(xiě)入字段集合 
    for (FieldInfo f : fields) {
      f.write(dout);
    }
    //12.寫(xiě)入方法計(jì)數(shù)值
    dout.writeShort(methods.size());
    //13.寫(xiě)入方法集合
    for (MethodInfo m : methods) {
      m.write(dout);
    }
    //14.寫(xiě)入屬性計(jì)數(shù)值, 代理類(lèi)class文件沒(méi)有屬性所以為0
    dout.writeShort(0);
  } catch (IOException e) {
    throw new InternalError("unexpected I/O Exception");
  }
  //轉(zhuǎn)換成二進(jìn)制數(shù)組輸出
  return bout.toByteArray();
}

可以看到generateClassFile()方法是按照Class文件結(jié)構(gòu)進(jìn)行動(dòng)態(tài)拼接的。什么是Class文件呢?在這里我們先要說(shuō)明下,我們平時(shí)編寫(xiě)的Java文件是以.java結(jié)尾的,在編寫(xiě)好了之后通過(guò)編譯器進(jìn)行編譯會(huì)生成.class文件,這個(gè).class文件就是Class文件。Java程序的執(zhí)行只依賴(lài)于Class文件,和Java文件是沒(méi)有關(guān)系的。這個(gè)Class文件描述了一個(gè)類(lèi)的信息,當(dāng)我們需要使用到一個(gè)類(lèi)時(shí),Java虛擬機(jī)就會(huì)提前去加載這個(gè)類(lèi)的Class文件并進(jìn)行初始化和相關(guān)的檢驗(yàn)工作,Java虛擬機(jī)能夠保證在你使用到這個(gè)類(lèi)之前就會(huì)完成這些工作,我們只需要安心的去使用它就好了,而不必關(guān)心Java虛擬機(jī)是怎樣加載它的。當(dāng)然,Class文件并不一定非得通過(guò)編譯Java文件而來(lái),你甚至可以直接通過(guò)文本編輯器來(lái)編寫(xiě)Class文件。在這里,JDK動(dòng)態(tài)代理就是通過(guò)程序來(lái)動(dòng)態(tài)生成Class文件的。我們?cè)俅位氐缴厦娴拇a中,可以看到,生成Class文件主要分為三步:

第一步:收集所有要生成的代理方法,將其包裝成ProxyMethod對(duì)象并注冊(cè)到Map集合中。

第二步:收集所有要為Class文件生成的字段信息和方法信息。

第三步:完成了上面的工作后,開(kāi)始組裝Class文件。

我們知道一個(gè)類(lèi)的核心部分就是它的字段和方法。我們重點(diǎn)聚焦第二步,看看它為代理類(lèi)生成了哪些字段和方法。在第二步中,按順序做了下面四件事。

1.為代理類(lèi)生成一個(gè)帶參構(gòu)造器,傳入InvocationHandler實(shí)例的引用并調(diào)用父類(lèi)的帶參構(gòu)造器。

2.遍歷代理方法Map集合,為每個(gè)代理方法生成對(duì)應(yīng)的Method類(lèi)型靜態(tài)域,并將其添加到fields集合中。

3.遍歷代理方法Map集合,為每個(gè)代理方法生成對(duì)應(yīng)的MethodInfo對(duì)象,并將其添加到methods集合中。

4.為代理類(lèi)生成靜態(tài)初始化方法,該靜態(tài)初始化方法主要是將每個(gè)代理方法的引用賦值給對(duì)應(yīng)的靜態(tài)字段。

通過(guò)以上分析,我們可以大致知道JDK動(dòng)態(tài)代理最終會(huì)為我們生成如下結(jié)構(gòu)的代理類(lèi):

public class Proxy0 extends Proxy implements UserDao {

  //第一步, 生成構(gòu)造器
  protected Proxy0(InvocationHandler h) {
    super(h);
  }

  //第二步, 生成靜態(tài)域
  private static Method m1;  //hashCode方法
  private static Method m2;  //equals方法
  private static Method m3;  //toString方法
  private static Method m4;  //...
  
  //第三步, 生成代理方法
  @Override
  public int hashCode() {
    try {
      return (int) h.invoke(this, m1, null);
    } catch (Throwable e) {
      throw new UndeclaredThrowableException(e);
    }
  }
  
  @Override
  public boolean equals(Object obj) {
    try {
      Object[] args = new Object[] {obj};
      return (boolean) h.invoke(this, m2, args);
    } catch (Throwable e) {
      throw new UndeclaredThrowableException(e);
    }
  }
  
  @Override
  public String toString() {
    try {
      return (String) h.invoke(this, m3, null);
    } catch (Throwable e) {
      throw new UndeclaredThrowableException(e);
    }
  }
  
  @Override
  public void save(User user) {
    try {
      //構(gòu)造參數(shù)數(shù)組, 如果有多個(gè)參數(shù)往后面添加就行了
      Object[] args = new Object[] {user};
      h.invoke(this, m4, args);
    } catch (Throwable e) {
      throw new UndeclaredThrowableException(e);
    }
  }
  
  //第四步, 生成靜態(tài)初始化方法
  static {
    try {
      Class c1 = Class.forName(Object.class.getName());
      Class c2 = Class.forName(UserDao.class.getName());  
      m1 = c1.getMethod("hashCode", null);
      m2 = c1.getMethod("equals", new Class[]{Object.class});
      m3 = c1.getMethod("toString", null);
      m4 = c2.getMethod("save", new Class[]{User.class});
      //...
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  
}

至此,經(jīng)過(guò)層層分析,深入探究JDK源碼,我們還原了動(dòng)態(tài)生成的代理類(lèi)的本來(lái)面目,之前心中存在的一些疑問(wèn)也隨之得到了很好的解釋

1.代理類(lèi)默認(rèn)繼承Porxy類(lèi),因?yàn)镴ava中只支持單繼承,所以JDK動(dòng)態(tài)代理只能去實(shí)現(xiàn)接口。

2.代理方法都會(huì)去調(diào)用InvocationHandler的invoke()方法,因此我們需要重寫(xiě)InvocationHandler的invoke()方法。

3.調(diào)用invoke()方法時(shí)會(huì)傳入代理實(shí)例本身,目標(biāo)方法和目標(biāo)方法參數(shù)。解釋了invoke()方法的參數(shù)是怎樣來(lái)的。

使用剛剛構(gòu)造出來(lái)的Proxy0作為代理類(lèi)再次進(jìn)行測(cè)試,可以看到最終的結(jié)果與使用JDK動(dòng)態(tài)生成的代理類(lèi)的效果是一樣的。再次驗(yàn)證了我們的分析是可靠且準(zhǔn)確的。至此,JDK動(dòng)態(tài)代理系列文章宣告結(jié)束。通過(guò)本系列的分析,筆者解決了心中長(zhǎng)久以來(lái)的疑惑,相信讀者們對(duì)JDK動(dòng)態(tài)代理的理解也更深了一步。但是紙上得來(lái)終覺(jué)淺,想要更好的掌握J(rèn)DK動(dòng)態(tài)代理技術(shù),讀者可參照本系列文章自行查閱JDK源碼,也可與筆者交流學(xué)習(xí)心得,指出筆者分析不當(dāng)?shù)牡胤?,共同學(xué)習(xí),共同進(jìn)步。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java設(shè)計(jì)模式中的適配器模式

    Java設(shè)計(jì)模式中的適配器模式

    這篇文章主要介紹了Java設(shè)計(jì)模式中的適配器模式, 適配器模式是將一個(gè)類(lèi)的接口適配成用戶所期待的,一個(gè)適配允許通常因?yàn)榻涌诓患嫒荻荒茉谝黄鸸ぷ鞯念?lèi)工作在一起,做法是將類(lèi)自己的接口包裹在一個(gè)已存在的類(lèi)中,需要的朋友可以參考下
    2024-01-01
  • Java開(kāi)發(fā)人員最常犯的5個(gè)錯(cuò)誤總結(jié)

    Java開(kāi)發(fā)人員最常犯的5個(gè)錯(cuò)誤總結(jié)

    作為一名java開(kāi)發(fā)程序員,不知道大家有沒(méi)有遇到過(guò)一些匪夷所思的bug。這些錯(cuò)誤通常需要您幾個(gè)小時(shí)才能解決。今天,小編總結(jié)一些常見(jiàn)的編碼錯(cuò)誤,然后給出解決方案。希望大家在日常編碼中能夠避免這樣的問(wèn)題
    2022-12-12
  • Java打印九九乘法表代碼詳情

    Java打印九九乘法表代碼詳情

    這篇文章主要介紹了Java打印九九乘法表,使用了雙重for循環(huán),使用do{}while()實(shí)現(xiàn)打印九九乘法表這些好玩的語(yǔ)法實(shí)現(xiàn),感興趣的小伙伴可參考下面文章內(nèi)容
    2021-09-09
  • Scala求和示例代碼

    Scala求和示例代碼

    這篇文章主要介紹了Scala求和示例代碼,需要的朋友可以參考下
    2019-06-06
  • spring cloud config 配置中心快速實(shí)現(xiàn)過(guò)程解析

    spring cloud config 配置中心快速實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了spring cloud config 配置中心快速實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • Java中stream是什么及如何使用

    Java中stream是什么及如何使用

    在Java中,Stream(流)是一種用于操作集合(Collection)、數(shù)組等數(shù)據(jù)源的API,Stream的主要作用是進(jìn)行數(shù)據(jù)的轉(zhuǎn)換、篩選、聚合等操作,可以極大地簡(jiǎn)化對(duì)數(shù)據(jù)的處理,本文給大家介紹Java中stream是什么?有什么作用?如何使用?感興趣的朋友一起看看吧
    2023-10-10
  • 開(kāi)源項(xiàng)目ERM模型轉(zhuǎn)jpa實(shí)體maven插件使用

    開(kāi)源項(xiàng)目ERM模型轉(zhuǎn)jpa實(shí)體maven插件使用

    這篇文章主要為大家介紹了開(kāi)源項(xiàng)目ERM模型轉(zhuǎn)jpa實(shí)體maven插件的使用說(shuō)明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03
  • 淺談讓@Value更方便的Spring自定義轉(zhuǎn)換類(lèi)

    淺談讓@Value更方便的Spring自定義轉(zhuǎn)換類(lèi)

    Spring為大家內(nèi)置了不少開(kāi)箱即用的轉(zhuǎn)換類(lèi),如字符串轉(zhuǎn)數(shù)字、字符串轉(zhuǎn)時(shí)間等,但有時(shí)候需要使用自定義的屬性,則需要自定義轉(zhuǎn)換類(lèi)了
    2021-06-06
  • SpringBoot整合Ldap的實(shí)現(xiàn)示例

    SpringBoot整合Ldap的實(shí)現(xiàn)示例

    本文主要介紹了SpringBoot整合Ldap的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-11-11
  • 基于Java實(shí)現(xiàn)圖形驗(yàn)證碼工具類(lèi)

    基于Java實(shí)現(xiàn)圖形驗(yàn)證碼工具類(lèi)

    這篇文章主要為大家詳細(xì)介紹了如何基于Java實(shí)現(xiàn)圖形驗(yàn)證碼工具類(lèi),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11

最新評(píng)論

精河县| 石城县| 澎湖县| 安龙县| 华安县| 丰台区| 常州市| 黔南| 临西县| 巴南区| 罗田县| 蚌埠市| 军事| 班戈县| 彰武县| 营山县| 黄陵县| 建德市| 广宁县| 台中市| 九江县| 炉霍县| 五莲县| 乐昌市| 红河县| 沁水县| 昭苏县| 丹江口市| 长治县| 读书| 龙胜| 鄱阳县| 宁陵县| 于都县| 子长县| 富裕县| 霍林郭勒市| 防城港市| 沁阳市| 唐河县| 高州市|