乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      有點(diǎn)干貨 | JDK、CGLIB動(dòng)態(tài)代理使用以及源碼分析

       小傅哥 2021-12-13

      微信公眾號:bugstack蟲洞棧
      沉淀、分享、成長,專注于原創(chuàng)專題案例,以最易學(xué)習(xí)編程的方式分享知識,讓自己和他人都能有所收獲。目前已完成的專題有;Netty4.x實(shí)戰(zhàn)專題案例、用Java實(shí)現(xiàn)JVM、基于JavaAgent的全鏈路監(jiān)控、手寫RPC框架、架構(gòu)設(shè)計(jì)專題案例[Ing]等。歡迎?Star和使用,你用劍🗡、我用刀🔪,好的代碼都很燒😏,望你不吝出招💨!

      前言介紹

      在Java中動(dòng)態(tài)代理是非常重要也是非常有用的一個(gè)技術(shù)點(diǎn),如果沒有動(dòng)態(tài)代理技術(shù)幾乎也就不會(huì)有各種優(yōu)秀框架的出現(xiàn),包括Spring。
      其實(shí)在動(dòng)態(tài)代理的使用中,除了我們平時(shí)用的Spring還有很多中間件和服務(wù)都用了動(dòng)態(tài)代理,例如;

      1. RPC通信框架Dubbo,在通信的時(shí)候由服務(wù)端提供一個(gè)接口描述信息的Jar,調(diào)用端進(jìn)行引用,之后在調(diào)用端引用后生成了對應(yīng)的代理類,當(dāng)執(zhí)行方法調(diào)用的時(shí)候,實(shí)際需要走到代理類向服務(wù)提供端發(fā)送請求信息,直至內(nèi)容回傳。
      2. 另外在使用Mybatis時(shí)候可以知道只需要定義一個(gè)接口,不需要實(shí)現(xiàn)具體方法就可以調(diào)用到Mapper中定義的數(shù)據(jù)庫操作信息了。這樣極大的簡化了代碼的開發(fā),又增強(qiáng)了效率。
      3. 最后不知道你自己是否嘗試過開發(fā)一些基于代理類的框架,以此來優(yōu)化業(yè)務(wù)代碼。也就是將業(yè)務(wù)代碼中非業(yè)務(wù)邏輯又通用性的功能抽離出來,開發(fā)為獨(dú)立的組件。推薦個(gè)案例,方便知道代理類的應(yīng)用:手寫RPC框架第三章《RPC中間件》

      代理方式

      動(dòng)態(tài)代理可以使用Jdk方式也可以使用CGLB,他們的區(qū)別,如下;

      類型機(jī)制回調(diào)方式適用場景效率
      JDK委托機(jī)制,代理類和目標(biāo)類都實(shí)現(xiàn)了同樣的接口,InvocationHandler持有目標(biāo)類,代理類委托InvocationHandler去調(diào)用目標(biāo)類的原始方法反射目標(biāo)類是接口類效率瓶頸在反射調(diào)用稍慢
      CGLIB繼承機(jī)制,代理類繼承了目標(biāo)類并重寫了目標(biāo)方法,通過回調(diào)函數(shù)MethodInterceptor調(diào)用父類方法執(zhí)行原始邏輯通過FastClass方法索引調(diào)用非接口類,非final類,非final方法第一次調(diào)用因?yàn)橐啥鄠€(gè)Class對象較JDK方式慢,多次調(diào)用因?yàn)橛蟹椒ㄋ饕^反射方式快,如果方法過多switch case過多其效率還需測試

      案例工程

      itstack-demo-test
      └── src
          ├── main
          │   └── java
          │       └── org.itstack.demo
          │           ├── proxy
          │           │└── cglib
          │           │    └── CglibProxy.java
          │           ├── jdk
          │           │├── reflect
          │           ││   ├── JDKInvocationHandler.java
          │           ││   └── JDKProxy.java
          │           │   └── util
          │           │    └── ClassLoaderUtils.java
          │           └── service
          │           ├── IUserService.java
          │           └── UserService.java
          └── test
              └── java
                  └── org.itstack.demo.test
                      └── ApiTest.java
      

      基礎(chǔ)接口和方法便于驗(yàn)證

      service/IUserService.java

      public interface IUserService {
      
          String queryUserNameById(String userId);
      
      }
      

      service/UserService.java

      public class UserService implements IUserService {
      
          public String queryUserNameById(String userId) {
              return "hi user " + userId;
          }
      
      }
      

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

      reflect/JDKInvocationHandler.java & 代理類反射調(diào)用

      • 實(shí)現(xiàn)InvocationHandler.invoke,用于方法增強(qiáng){監(jiān)控、執(zhí)行其他業(yè)務(wù)邏輯、遠(yuǎn)程調(diào)用等}
      • 如果有需要額外的參數(shù)可以提供構(gòu)造方法
      public class JDKInvocationHandler implements InvocationHandler {
      
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println(method.getName());
              return "我被JDKProxy代理了";
          }
      
      }
      

      reflect/JDKProxy.java & 定義一個(gè)代理類獲取的服務(wù)

      • Proxy.newProxyInstance 來實(shí)際生成代理類,過程如下;
        1. Class<?> cl = getProxyClass0(loader, intfs); 查找或生成指定的代理類
        2. proxyClassCache.get(loader, interfaces); 代理類的緩存中獲取
        3. subKeyFactory.apply(key, parameter) 繼續(xù)下一層
        4. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); 生成代理類的字節(jié)碼
      public class JDKProxy {
      
          public static <T> T getProxy(Class<T> interfaceClass) throws Exception {
              InvocationHandler handler = new JDKInvocationHandler();
              ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();
              T result = (T) Proxy.newProxyInstance(classLoader, new Class[]{interfaceClass}, handler);
              return result;
          }
      
      }
      

      ApiTest.test_proxy_jdk() & 執(zhí)行調(diào)用并輸出反射類的字節(jié)碼

      • 代理后調(diào)用方法驗(yàn)證
      • 通過使用ProxyGenerator.generateProxyClass獲取實(shí)際的字節(jié)碼,查看代理類的內(nèi)容
      @Test
      public void test_proxy_jdk() throws Exception {
      
      IUserService proxy = (IUserService) JDKProxy.getProxy(ClassLoaderUtils.forName("org.itstack.demo.service.IUserService"));
      String userName = proxy.queryUserNameById("10001");
      System.out.println(userName);
      
      String name = "ProxyUserService";
      byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{IUserService.class});
      
      // 輸出類字節(jié)碼
      FileOutputStream out = null;
      try {
      out = new FileOutputStream(name + ".class");
      System.out.println((new File("")).getAbsolutePath());
      out.write(data);
      } catch (FileNotFoundException e) {
      e.printStackTrace();
      } catch (IOException e) {
      e.printStackTrace();
      } finally {
      if (null != out) try {
      out.close();
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      
      }
      

      輸出結(jié)果

      queryUserNameById
      我被JDKProxy代理了
      

      將生成的代理類進(jìn)行反編譯jd-gui

      部分內(nèi)容抽取,可以看到比較核心的方法,也就是我們在調(diào)用的時(shí)候走到了這里

      public final String queryUserNameById(String paramString)
          throws 
      {
      try
      {
        return (String)this.h.invoke(this, m3, new Object[] { paramString });
      }
      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") });
        m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        m3 = Class.forName("org.itstack.demo.service.IUserService").getMethod("queryUserNameById", new Class[] { Class.forName("java.lang.String") });
        return;
      }
      catch (NoSuchMethodException localNoSuchMethodException)
      {
        throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
      }
      catch (ClassNotFoundException localClassNotFoundException)
      {
        throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
      }
      }
      

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

      cglib/CglibProxy.java

      • 提供構(gòu)造方法,生成CGLIB的代理類,回調(diào)this
      • intercept可以進(jìn)行方法的增強(qiáng),處理相關(guān)業(yè)務(wù)邏輯
      • CGLIB是通過ASM來操作字節(jié)碼生成類
      public class CglibProxy implements MethodInterceptor {
      
          public Object newInstall(Object object) {
              return Enhancer.create(object.getClass(), this);
          }
      
          public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
              System.out.println("我被CglibProxy代理了");
              return methodProxy.invokeSuper(o, objects);
          }
      
      }
      

      ApiTest.test_proxy_cglib() & 調(diào)用代理類

      @Test
      public void test_proxy_cglib() {
          CglibProxy cglibProxy = new CglibProxy();
          UserService userService = (UserService) cglibProxy.newInstall(new UserService());
          String userName = userService.queryUserNameById("10001");
          System.out.println(userName);
      }
      

      輸出結(jié)果

      我被CglibProxy代理了
      hi user 10001
      

      綜上總結(jié)

      • 在我們實(shí)際使用中兩種方式都用所有使用,也可以依照不同的訴求進(jìn)行選擇
      • 往往動(dòng)態(tài)代理會(huì)和注解共同使用,代理類拿到以后獲取方法的注解,并做相應(yīng)的業(yè)務(wù)操作
      • 有時(shí)候你是否會(huì)遇到增加AOP不生效,因?yàn)橛袝r(shí)候有些類是被代理操作的,并沒有執(zhí)行你的自定義注解也就是切面

      微信公眾號:bugstack蟲洞棧,歡迎關(guān)注&獲取源碼

        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多