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

          Java 動(dòng)態(tài)代理的底層原理

          共 25868字,需瀏覽 52分鐘

           ·

          2022-07-11 23:50

          7點(diǎn)擊關(guān)注公眾號(hào):互聯(lián)網(wǎng)架構(gòu)師,后臺(tái)回復(fù) 2T獲取2TB學(xué)習(xí)資源!
          上一篇:Alibaba開(kāi)源內(nèi)網(wǎng)高并發(fā)編程手冊(cè).pdf

          原文:blog.csdn.net/weixin_45505313


          1.代理模式

          代理模式是常用的設(shè)計(jì)模式之一,其特征是代理類(lèi)與被代理類(lèi)有相同的接口,代理類(lèi)可以為被代理類(lèi)方法執(zhí)行進(jìn)行前置后置處理,增強(qiáng)被代理類(lèi)方法

          代理模式的類(lèi)結(jié)構(gòu)通常如上圖所示,代理類(lèi)與被代理類(lèi)之間會(huì)存在關(guān)聯(lián)關(guān)系,一個(gè)代理類(lèi)的對(duì)象持有一個(gè)被代理類(lèi)的對(duì)象。代理類(lèi)的對(duì)象本身并不真正實(shí)現(xiàn)服務(wù),而是通過(guò)調(diào)用被代理類(lèi)對(duì)象的相關(guān)方法來(lái)提供特定的服務(wù)

          2. 動(dòng)態(tài)代理使用

          代理類(lèi)并不是在 Java 代碼中定義,而是在運(yùn)行時(shí)根據(jù)在 Java 代碼中的“指示”動(dòng)態(tài)生成(字節(jié)碼由JVM在運(yùn)行時(shí)動(dòng)態(tài)生成而非預(yù)存在任何一個(gè) .class 文件中), 這種在程序運(yùn)行時(shí)創(chuàng)建代理類(lèi)的代理方式被稱(chēng)為動(dòng)態(tài)代理,它的優(yōu)勢(shì)在于可以方便地對(duì)代理類(lèi)的函數(shù)進(jìn)行統(tǒng)一處理。

          這是因?yàn)樗斜淮韴?zhí)行的方法,都是通過(guò)InvocationHandler#invoke()方法調(diào)用,相當(dāng)于給被代理類(lèi)所有方法套了一層殼,所以只要在這個(gè)方法中統(tǒng)一處理,就可以對(duì)所有被代理的方法進(jìn)行相同的操作了

          以下代碼展示了動(dòng)態(tài)代理的簡(jiǎn)單使用,其基本步驟如下:

          • 定義一個(gè)公共接口,本例中為 IHello,接口中有一個(gè)抽象方法
          • 定義一個(gè)實(shí)現(xiàn)了公共接口的實(shí)體類(lèi)作為被代理類(lèi),本例中被代理類(lèi) Hello實(shí)現(xiàn)了 IHello接口,重寫(xiě)了接口中的抽象方法
          • 定義一個(gè)實(shí)現(xiàn)了 InvocationHandler 接口的方法攔截類(lèi),重寫(xiě) invoke() 方法實(shí)現(xiàn)攔截到被代理類(lèi)方法執(zhí)行時(shí)候的處理邏輯
          • 通過(guò) Proxy.newProxyInstance() 方法生成代理對(duì)象,持有代理對(duì)象之后執(zhí)行接口方法即可
          public class ServiceProxy {

              public interface IHello {
                  String sayHi();
              }

              public static class Hello implements IHello {
                  @Override
                  public String sayHi() {
                      return "Hello";
                  }
              }

              // 動(dòng)態(tài)代理類(lèi)
              public static class ProxyHandler<Timplements InvocationHandler {
                  private T origin;

                  public ProxyHandler(T origin) {
                      this.origin = origin;
                  }

                 /**
                   * @param o 代理對(duì)象引用
                   * @param method 正在執(zhí)行目標(biāo)的方法
                   * @param objects 目標(biāo)方法執(zhí)行時(shí)的入?yún)?br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">        */

                  @Override
                  public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                      String s = "proxy";
                      s += method.invoke(origin, objects);
                      return s;
                  }
              }

              public static void main(String[] args) {
                  IHello IHello = (IHello) getInstance(IHello.classnew ProxyHandler<>(new Hello()));

                  System.out.println(IHello.toString());

                  generateProxyClass();
              }

              // 創(chuàng)建代理對(duì)象
              public static <T> Object getInstance(Class<T> clazz, ProxyHandler<T> handler) {
                  return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, handler);
              }

              private static void generateProxyClass() {
                  byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Hello.class.getInterfaces());
                  String path = "/Users/nathan.yang/workspace/algorithm_Java/out/StuProxy.class";
                  try (FileOutputStream fos = new FileOutputStream(path)) {
                      fos.write(classFile);
                      fos.flush();
                      System.out.println("代理類(lèi)文件寫(xiě)入成功");
                  } catch (Exception e) {
                      System.out.println("寫(xiě)文件錯(cuò)誤");
                  }
              }
          }

          3. 動(dòng)態(tài)代理原理

          1.Proxy#newProxyInstance() 方法是動(dòng)態(tài)代理的入口,其生成動(dòng)態(tài)代理對(duì)象主要有以下幾個(gè)步驟:

          • getProxyClass0() 方法生成代理類(lèi)
          • 獲取到代理類(lèi)后將 InvocationHandler 對(duì)象入?yún)ⅲ瓷湔{(diào)用構(gòu)造方法生成動(dòng)態(tài)代理對(duì)象
          public static Object newProxyInstance(ClassLoader loader,
                                                 Class<?>[] interfaces,
                                                 InvocationHandler h)

               throws IllegalArgumentException
           
          {
               Objects.requireNonNull(h);

               final Class<?>[] intfs = interfaces.clone();
               final SecurityManager sm = System.getSecurityManager();
               if (sm != null) {
                   checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
               }

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

               Class<?> cl = getProxyClass0(loader, intfs);

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

               try {
                   if (sm != null) {
                       checkNewProxyPermission(Reflection.getCallerClass(), cl);
                   }

                   final Constructor<?> cons = cl.getConstructor(constructorParams);
                   final InvocationHandler ih = h;
                   if (!Modifier.isPublic(cl.getModifiers())) {
                       AccessController.doPrivileged(new PrivilegedAction<Void>() {
                           public Void run() {
                               cons.setAccessible(true);
                               return null;
                           }
                       });
                   }
                   return cons.newInstance(new Object[]{h});
               } catch (IllegalAccessException|InstantiationException e) {
                   throw new InternalError(e.toString(), e);
               } catch (InvocationTargetException e) {
                   Throwable t = e.getCause();
                   if (t instanceof RuntimeException) {
                       throw (RuntimeException) t;
                   } else {
                       throw new InternalError(t.toString(), t);
                   }
               } catch (NoSuchMethodException e) {
                   throw new InternalError(e.toString(), e);
               }
           }

          2.Proxy#getProxyClass0() 方法其實(shí)是從一個(gè) WeakCache 中去獲取代理類(lèi),其獲取邏輯是如果緩存類(lèi)中沒(méi)有代理類(lèi)的話就調(diào)用ProxyClassFactory#apply(),通過(guò)代理類(lèi)工廠去即時(shí)生成一個(gè)代理類(lèi),其步驟如下:

          • 首先通過(guò)指定的類(lèi)加載器去驗(yàn)證目標(biāo)接口是否可被其加載
          • 通過(guò)接口所在包等條件決定代理類(lèi)所在包及代理類(lèi)的全限定名稱(chēng),代理類(lèi)名稱(chēng)是包名+$Proxy+id
          • 通過(guò) ProxyGenerator.generateProxyClass() 生成字節(jié)碼數(shù)組,然后調(diào)用 native 方法 defineClass0() 將其動(dòng)態(tài)生成的代理類(lèi)字節(jié)碼加載到內(nèi)存中
          private static Class<?> getProxyClass0(ClassLoader loader,
                                                  Class<?>... interfaces) {
               if (interfaces.length > 65535) {
                   throw new IllegalArgumentException("interface limit exceeded");
               }

               // If the proxy class defined by the given loader implementing
               // the given interfaces exists, this will simply return the cached copy;
               // otherwise, it will create the proxy class via the ProxyClassFactory
               return proxyClassCache.get(loader, interfaces);
           }
           
          public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

                   Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                   for (Class<?> intf : interfaces) {
                       /*
                        * Verify that the class loader resolves the name of this
                        * interface to the same Class object.
                        */

                       Class<?> interfaceClass = null;
                       try {
                           interfaceClass = Class.forName(intf.getName(), false, loader);
                       } catch (ClassNotFoundException e) {
                       }
                       if (interfaceClass != intf) {
                           throw new IllegalArgumentException(
                               intf + " is not visible from class loader");
                       }
                       /*
                        * Verify that the Class object actually represents an
                        * interface.
                        */

                       if (!interfaceClass.isInterface()) {
                           throw new IllegalArgumentException(
                               interfaceClass.getName() + " is not an interface");
                       }
                       /*
                        * Verify that this interface is not a duplicate.
                        */

                       if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                           throw new IllegalArgumentException(
                               "repeated interface: " + interfaceClass.getName());
                       }
                   }

                   String proxyPkg = null;     // package to define proxy class in
                   int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

                   /*
                    * Record the package of a non-public proxy interface so that the
                    * proxy class will be defined in the same package.  Verify that
                    * all non-public proxy interfaces are in the same package.
                    */

                   for (Class<?> intf : interfaces) {
                       int flags = intf.getModifiers();
                       if (!Modifier.isPublic(flags)) {
                           accessFlags = Modifier.FINAL;
                           String name = intf.getName();
                           int n = name.lastIndexOf('.');
                           String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                           if (proxyPkg == null) {
                               proxyPkg = pkg;
                           } else if (!pkg.equals(proxyPkg)) {
                               throw new IllegalArgumentException(
                                   "non-public interfaces from different packages");
                           }
                       }
                   }

                   if (proxyPkg == null) {
                       // if no non-public proxy interfaces, use com.sun.proxy package
                       proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                   }

                   /*
                    * Choose a name for the proxy class to generate.
                    */

                   long num = nextUniqueNumber.getAndIncrement();
                   String proxyName = proxyPkg + proxyClassNamePrefix + num;

                   /*
                    * Generate the specified proxy class.
                    */

                   byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                       proxyName, interfaces, accessFlags);
                   try {
                       return defineClass0(loader, proxyName,
                                           proxyClassFile, 0, proxyClassFile.length);
                   } catch (ClassFormatError e) {
                       /*
                        * A ClassFormatError here means that (barring bugs in the
                        * proxy class generation code) there was some other
                        * invalid aspect of the arguments supplied to the proxy
                        * class creation (such as virtual machine limitations
                        * exceeded).
                        */

                       throw new IllegalArgumentException(e.toString());
                   }
               }

          3.反射獲取到代理類(lèi)參數(shù)為 InvocationHandler.class 的構(gòu)造器,其實(shí)也就是 Proxy 的帶參構(gòu)造器,調(diào)用構(gòu)造器cons.newInstance(new Object[]{h})生成代理對(duì)象

          protected Proxy(InvocationHandler h) {
               Objects.requireNonNull(h);
               this.h = h;
           }

          4.通過(guò)以下代碼可以將 JVM 中加載的代理類(lèi)輸出成 class 文件,之后就可以使用反編譯工具查看代理類(lèi)的源碼

          private static void generateProxyClass() {
               byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Hello.class.getInterfaces());
               String path = "/Users/nathan/workspace/algorithm_Java/out/StuProxy.class";
               try (FileOutputStream fos = new FileOutputStream(path)) {
                   fos.write(classFile);
                   fos.flush();
                   System.out.println("代理類(lèi)文件寫(xiě)入成功");
               } catch (Exception e) {
                   System.out.println("寫(xiě)文件錯(cuò)誤");
               }
           }

          5.生成的代理類(lèi)源碼如下,很明顯可以看到該類(lèi)實(shí)現(xiàn)動(dòng)態(tài)代理的原理:

          • 通過(guò) static 代碼塊將被代理類(lèi)中每一個(gè)方法封裝為 Method 對(duì)象,生成方法表
          • 代理類(lèi)對(duì)象執(zhí)行被代理類(lèi)同名方法時(shí),通過(guò)其父類(lèi)Proxy保留的指向InvocationHandler對(duì)象的引用調(diào)用 InvocationHandler#invoke() 方法,完成動(dòng)態(tài)代理
          public final class $Proxy0 extends Proxy implements IHello {
           private static Method m1;
           private static Method m3;
           private static Method m2;
           private static Method m0;

           public $Proxy0(InvocationHandler var1) throws  {
               super(var1);
           }
           
           public final String sayHi() throws  {
               try {
                 // 父類(lèi) Proxy 保留的指向 InvocationHandler 對(duì)象的引用調(diào)用 invoke() 方法
                   return (String)super.h.invoke(this, m3, (Object[])null);
               } catch (RuntimeException | Error var2) {
                   throw var2;
               } catch (Throwable var3) {
                   throw new UndeclaredThrowableException(var3);
               }
           }

           public final String toString() throws  {
               try {
                   return (String)super.h.invoke(this, m2, (Object[])null);
               } catch (RuntimeException | Error var2) {
                   throw var2;
               } catch (Throwable var3) {
                   throw new UndeclaredThrowableException(var3);
               }
           }

          ......

          // 方法表
           static {
               try {
                   m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                   m3 = Class.forName("ServiceProxy$IHello").getMethod("sayHi");
                   m2 = Class.forName("java.lang.Object").getMethod("toString");
                   m0 = Class.forName("java.lang.Object").getMethod("hashCode");
               } catch (NoSuchMethodException var2) {
                   throw new NoSuchMethodError(var2.getMessage());
               } catch (ClassNotFoundException var3) {
                   throw new NoClassDefFoundError(var3.getMessage());
               }
           }
          }




          -End-

          最后,關(guān)注公眾號(hào)互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)回復(fù):2T,可以獲取我整理的 Java 系列面試題和答案,非常齊全


          正文結(jié)束


          推薦閱讀 ↓↓↓

          1.MySQL 被超越了 !!!

          2.從零開(kāi)始搭建創(chuàng)業(yè)公司后臺(tái)技術(shù)棧

          3.程序員一般可以從什么平臺(tái)接私活?

          4.清華大學(xué):2021 元宇宙研究報(bào)告!

          5.為什么國(guó)內(nèi) 996 干不過(guò)國(guó)外的 955呢?

          6.這封“領(lǐng)導(dǎo)痛批95后下屬”的郵件,句句扎心!                                  

          7.15張圖看懂瞎忙和高效的區(qū)別!

          瀏覽 50
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  人人射人人摸 | 成人激情自拍 | 另类TS人妖一区二区三区 | 国广富姐搭讪坐顺风车 | 97超碰在|