<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)代理的底層原理

          共 25694字,需瀏覽 52分鐘

           ·

          2022-07-11 23:30

          1. 代理模式

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

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

          2. 動(dòng)態(tà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)建代理類的代理方式被稱為動(dòng)態(tài)代理,它的優(yōu)勢(shì)在于可以方便地對(duì)代理類的函數(shù)進(jìn)行統(tǒng)一處理。

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

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

          • 定義一個(gè)公共接口,本例中為 IHello,接口中有一個(gè)抽象方法
          • 定義一個(gè)實(shí)現(xiàn)了公共接口的實(shí)體類作為被代理類,本例中被代理類 Hello實(shí)現(xiàn)了 IHello接口,重寫(xiě)了接口中的抽象方法
          • 定義一個(gè)實(shí)現(xiàn)了 InvocationHandler 接口的方法攔截類,重寫(xiě) invoke() 方法實(shí)現(xiàn)攔截到被代理類方法執(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)代理類
              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("代理類文件寫(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() 方法生成代理類
          • 獲取到代理類后將 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 中去獲取代理類,其獲取邏輯是如果緩存類中沒(méi)有代理類的話就調(diào)用ProxyClassFactory#apply(),通過(guò)代理類工廠去即時(shí)生成一個(gè)代理類,其步驟如下:

          • 首先通過(guò)指定的類加載器去驗(yàn)證目標(biāo)接口是否可被其加載
          • 通過(guò)接口所在包等條件決定代理類所在包及代理類的全限定名稱,代理類名稱是包名+$Proxy+id
          • 通過(guò) ProxyGenerator.generateProxyClass() 生成字節(jié)碼數(shù)組,然后調(diào)用 native 方法 defineClass0() 將其動(dòng)態(tà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.反射獲取到代理類參數(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 中加載的代理類輸出成 class 文件,之后就可以使用反編譯工具查看代理類的源碼

          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("代理類文件寫(xiě)入成功");
               } catch (Exception e) {
                   System.out.println("寫(xiě)文件錯(cuò)誤");
               }
           }

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

          • 通過(guò) static 代碼塊將被代理類中每一個(gè)方法封裝為 Method 對(duì)象,生成方法表
          • 代理類對(duì)象執(zhí)行被代理類同名方法時(shí),通過(guò)其父類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 {
                 // 父類 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());
               }
           }
          }

          來(lái)源:https://blog.csdn.net/weixin_45505313

          推薦閱讀:

          世界的真實(shí)格局分析,地球人類社會(huì)底層運(yùn)行原理

          不是你需要中臺(tái),而是一名合格的架構(gòu)師(附各大廠中臺(tái)建設(shè)PPT)

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          華為干部與人才發(fā)展手冊(cè)(附PPT)

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺(tái)實(shí)踐】華為大數(shù)據(jù)中臺(tái)架構(gòu)分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實(shí)施數(shù)字化轉(zhuǎn)型(附PPT)

          超詳細(xì)280頁(yè)Docker實(shí)戰(zhàn)文檔!開(kāi)放下載

          華為大數(shù)據(jù)解決方案(PPT)



          瀏覽 32
          點(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>
                  俺来也听听婷婷 | 俺也去五月天 | seseav | 91XXx欧美性缓 | 在线免费看亚洲区中文字幕 |