<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從頭捋了一遍 JDK動態(tài)代理,收獲頗豐!

          共 13509字,需瀏覽 28分鐘

           ·

          2021-06-19 20:49

          動態(tài)代理,這個詞在Java的世界里面經(jīng)常被提起,尤其是對于部分(這里強(qiáng)調(diào)“部分”二字,因為有做了一兩年就成大神的,實力強(qiáng)的令人發(fā)指,這類人無疑是非常懂動態(tài)代理這點小伎倆的)做了一兩年新人來說,總是摸不清楚來龍去脈,一兩年是個坎,為什么是一兩年,才入門的新人可能對這東西沒什么感覺,沒到這一步,做了很久開發(fā)的人顯然是明白這其中原理的,而做了一兩年的,知其然而不知其所以然,所以一兩年工作經(jīng)驗的人很多是很茫然的。

          那么,這里就相對比較比較深入一點的介紹JDK動態(tài)代理的原理。這樣子介紹完,明白了其中的道理,我相信你會永遠(yuǎn)記得JDK動態(tài)代理的思想。順帶一句,cglib做的事兒和JDK動態(tài)代理做的事兒從結(jié)局上來說差不多,方式不太一樣。

          1、先從JDK的源代碼說起,動態(tài)代理這部分源碼,Oracle版本和OpenJDK的源碼是不太一樣的,貌似Oracle版本最核心那點東西沒開源,F(xiàn)3進(jìn)去我反正是找不到,我也懶得去找,但是原理都是一致的,這里就挑選OpenJDK的。

          我們回顧一下JDK動態(tài)代理,先說宏觀原理,相信都懂,使用JDK動態(tài)代理最常見,至少對于我來說就是Spring的AOP部分,并且是AOP部分的聲明式事務(wù)部分。

          a、定義一個接口Car:

          public interface Car {
              void drive(String driverName, String carName);
          }

          b、定義接口Car的一個實現(xiàn)類Audi:

          public class Audi implements Car {

              @Override
              public void drive(String driverName, String carName) {
                  System.err.println("Audi is driving... " + "driverName: " + driverName + ", carName" + carName);
              }
          }

          c、定義一個動態(tài)調(diào)用的控制器CarHandler:

          public class CarHandler implements InvocationHandler {
              
              private Car car;
              
              public CarHandler(Car car) {
                  this.car = car;
              }
              
              @Override
              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  System.err.println("before");
                  method.invoke(car, args);
                  System.err.println("after");
                  return null;
              }

          }

          d、測試類ProxyTest:

          public class ProxyTest {
              
              @Test
              public void proxyTest() throws Exception {
                  Car audi = (Car) Proxy.newProxyInstance(Car.class.getClassLoader(), new Class<?>[] {Car.class}, new CarHandler(new Audi()));
                  audi.drive("name1""audi");
              }
          }

          e、輸出結(jié)果:

          before
          Audi is driving... driverName: name1, carNameaudi
          after

          上面這段,相信大家都懂,也明白原理,這就是所謂的知其然,但是不一定都能知其所以然。接下來就解釋下“知其所以然”。

          進(jìn)入到Proxy類的newProxyInstance方法:

          public static Object newProxyInstance(ClassLoader loader,
                                                    Class<?>[] interfaces,
                                                    InvocationHandler h)

                  throws IllegalArgumentException
              
          {
                  if (h == null) {
                      throw new NullPointerException();
                  }

                  /*
                   * Look up or generate the designated proxy class.
                   */

                  Class<?> cl = getProxyClass(loader, interfaces);

                  /*
                   * Invoke its constructor with the designated invocation handler.
                   */

                  try {
                      Constructor cons = cl.getConstructor(constructorParams);
                      return cons.newInstance(new Object[] { h });
                  } catch (NoSuchMethodException e) {
                      throw new InternalError(e.toString());
                  } catch (IllegalAccessException e) {
                      throw new InternalError(e.toString());
                  } catch (InstantiationException e) {
                      throw new InternalError(e.toString());
                  } catch (InvocationTargetException e) {
                      throw new InternalError(e.toString());
                  }
              }

          關(guān)鍵的3行:

          // 創(chuàng)建代理類
          Class<?> cl = getProxyClass(loader, interfaces);
          // 實例化代理對象
          Constructor cons = cl.getConstructor(constructorParams);

          返回的是代理類的實例化對象。接下來的調(diào)用就很清晰了。

          那么,JDK動態(tài)代理最核心的關(guān)鍵就是這個方法:

          Class<?> cl = getProxyClass(loader, interfaces);

          進(jìn)入該方法,這個方法很長,前面很多都是鋪墊,在方法的最后調(diào)用了一個方法:

          byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                              proxyName, interfaces);

          這個方法就是產(chǎn)生代理對象的方法。我們先不看前后,只關(guān)注這一個方法,我們自己來寫一個該方法:

          public class ProxyTest {
              
              @SuppressWarnings("resource")
              @Test
              public void proxyTest() throws Exception {
                  byte[] bs = ProxyGenerator.generateProxyClass("AudiImpl"new Class<?>[] {Car.class});
                  new FileOutputStream(new File("d:/AudiImpl.class")).write(bs);
              }
          }

          于是,我們就在D盤里面看到了一個叫做AudiImpl.class的文件,對該文件進(jìn)行反編譯,得到下面這個類:

          public final class AudiImpl extends Proxy implements Car {

              private static final long serialVersionUID = 5351158173626517207L;

              private static Method m1;
              private static Method m3;
              private static Method m0;
              private static Method m2;

              public AudiImpl(InvocationHandler paramInvocationHandler) {
                  super(paramInvocationHandler);
              }

              public final boolean equals(Object paramObject) {
                  try {
                      return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
                  } catch (Error | RuntimeException localError) {
                      throw localError;
                  } catch (Throwable localThrowable) {
                      throw new UndeclaredThrowableException(localThrowable);
                  }
              }

              public final void drive(String paramString1, String paramString2) {
                  try {
                      this.h.invoke(this, m3, new Object[] { paramString1, paramString2 });
                      return;
                  } catch (Error | RuntimeException localError) {
                      throw localError;
                  } catch (Throwable localThrowable) {
                      throw new UndeclaredThrowableException(localThrowable);
                  }
              }

              public final int hashCode() {
                  try {
                      return ((Integer) this.h.invoke(this, m0, null)).intValue();
                  } catch (Error | RuntimeException localError) {
                      throw localError;
                  } catch (Throwable localThrowable) {
                      throw new UndeclaredThrowableException(localThrowable);
                  }
              }

              public final String toString() {
                  try {
                      return (String) this.h.invoke(this, m2, null);
                  } catch (Error | RuntimeException localError) {
                      throw localError;
                  } catch (Throwable localThrowable) {
                      throw new UndeclaredThrowableException(localThrowable);
                  }
              }

              static {
                  try {
                      m1 = Class.forName("java.lang.Object").getMethod("equals",
                              new Class[] { Class.forName("java.lang.Object") });
                      m3 = Class.forName("com.mook.core.service.Car").getMethod("drive",
                              new Class[] { Class.forName("java.lang.String"), Class.forName("java.lang.String") });
                      m0 = Class.forName("java.lang.Object").getMethod("hashCode"new Class[0]);
                      m2 = Class.forName("java.lang.Object").getMethod("toString"new Class[0]);
                  } catch (NoSuchMethodException localNoSuchMethodException) {
                      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
                  } catch (ClassNotFoundException localClassNotFoundException) {
                      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
                  }
              }
          }

          這個時候,JDK動態(tài)代理所有的秘密都暴露在了你的面前,當(dāng)我們調(diào)用drive方法的時候,實際上是把方法名稱傳給控制器,然后執(zhí)行控制器邏輯。這就實現(xiàn)了動態(tài)代理。

          Spring AOP有兩種方式實現(xiàn)動態(tài)代理,如果基于接口編程,默認(rèn)就是JDK動態(tài)代理,否則就是cglib方式,另外spring的配置文件里面也可以設(shè)置使用cglib來做動態(tài)代理,關(guān)于二者的性能問題,網(wǎng)上也是眾說紛紜,不過我個人的觀點,性能都不是問題,不太需要去糾結(jié)這一點性能問題。

          事實上,如果你明白這一點,當(dāng)你去閱讀mybatis源碼的時候是很有幫助的,mybatis的接口方式做方法查詢就充分利用了這里的JDK動態(tài)代理。否則如果不明白原理,看mybatis的源碼的接口方式是很費(fèi)勁的,當(dāng)然了,這只是mybatis利用動態(tài)代理的冰山一角,要完全看懂mybaits源碼還有其他的許多難點,比如mybatis是以xml文件來配置sql語句的。最后,歡迎關(guān)注公眾號Java筆記蝦,后臺回復(fù)“后端面試”,送你一份面試題寶典!

          (感謝閱讀,希望對你所有幫助)
          來源:cnblogs.com/dreamroute/p/5273888.html

          最近給大家找了  百萬級電商


          資源,怎么領(lǐng)取?


          掃二維碼,加我微信,回復(fù):百萬級電商

           注意,不要亂回復(fù) 

          沒錯,不是機(jī)器人
          記得一定要等待,等待才有好東西
          瀏覽 53
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操逼日 | 欧美操屄操逼 | 天天干天天操综合网 | 俺来也网 | 精品熟人一区二区三区四区 |