1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        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開源內(nèi)網(wǎng)高并發(fā)編程手冊(cè).pdf

        原文:blog.csdn.net/weixin_45505313


        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ù),而是通過調(diào)用被代理類對(duì)象的相關(guān)方法來提供特定的服務(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í)行的方法,都是通過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接口,重寫了接口中的抽象方法
        • 定義一個(gè)實(shí)現(xiàn)了 InvocationHandler 接口的方法攔截類,重寫 invoke() 方法實(shí)現(xiàn)攔截到被代理類方法執(zhí)行時(shí)候的處理邏輯
        • 通過 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("代理類文件寫入成功");
                } catch (Exception e) {
                    System.out.println("寫文件錯(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 中去獲取代理類,其獲取邏輯是如果緩存類中沒有代理類的話就調(diào)用ProxyClassFactory#apply(),通過代理類工廠去即時(shí)生成一個(gè)代理類,其步驟如下:

        • 首先通過指定的類加載器去驗(yàn)證目標(biāo)接口是否可被其加載
        • 通過接口所在包等條件決定代理類所在包及代理類的全限定名稱,代理類名稱是包名+$Proxy+id
        • 通過 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.通過以下代碼可以將 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("代理類文件寫入成功");
             } catch (Exception e) {
                 System.out.println("寫文件錯(cuò)誤");
             }
         }

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

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




        -End-

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


        正文結(jié)束


        推薦閱讀 ↓↓↓

        1.MySQL 被超越了 ?。?!

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

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

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

        5.為什么國(guó)內(nèi) 996 干不過國(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)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲精彩中文字幕字幕 | 淫色视频在线播出 | 国产三级片中文字幕 | 亚州精品天堂中文字幕 | 日韩黄片无码 | 吃小男生初精h黄 | 边摸边吃奶边做爽gif动态图 | 国产黄a| 噜噜色图 | 久热草 |