• 设计模式——代理模式


      什么是代理?

      通俗的语言就是 一件事委托给别人代办,别人能办的更好,更完善(在此功能基础上增加了其他的操作 比如处理消息之类的东西)。

      代理模式分为静态代理和动态代理

      1. 代理类比较

        由程序员创建或者特定工具自动生成源代码,在进行编译。在运行前代理类的.class就存在了。

        在程序运行时,运用反射机制动态创建而成。而非提前书写好的。

      2. 优劣

        静态代理只需要关注逻辑本身,保证业务逻辑的重用性,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,需要为每种方法进行代理,如果接口增加了方法实现类都要实现这个方法,增加了维护的复杂度。

        动态代理接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。接口方法较多的时候可以进行灵活处理,不需要像静态代理那样每一个方法进行中转。

        动态代理应用职责更加单一,复用性更强。

        静态代理

      以购票为例子

        // 接口能买票的人
        interface Person{
            void buyTicket();
        }
        // 要买票的人
        class NeedPerson implements Person{
            @Override
            public void buyTicket() {
                System.out.println("我要买票");
            }
            
        }
        // 代理的角色
        class Agent implements Person{
            Person needPerson;
            public Agent(Person needPerson) {
                this.needPerson = needPerson;
            }
            @Override
            public void buyTicket() {
                System.out.println("我有渠道买票");
                needPerson.buyTicket();
                System.out.println("下次再来");
            }
            
        }
        public static void main(String[] args) {
            Person agent = new Agent(new NeedPerson());
            agent.buyTicket();
        }    

      

      动态代理

        // 接口能买票的人
        interface Person {
            void buyTicket();
        }
    
        // 要买票的人
        class NeedPerson implements Person {
            @Override
            public void buyTicket() {
                System.out.println("我要买票");
            }
        }
    
        // 代理卖票
        class BuyHandler implements InvocationHandler {
            Person needPerson;
    
            public BuyHandler(Person needPerson) {
                this.needPerson = needPerson;
            }
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("我有渠道买票");
                method.invoke(needPerson, args);
                System.out.println("下次再来");
                return null;
            }
    
        }
    
        public static void main(String[] args) {
            DynamicProxy dynamicProxy = new DynamicProxy();
            Person landlord = new NeedPerson();
            Person proxy = (Person) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] { Person.class }, new BuyHandler(landlord));
            proxy.buyTicket();
        }

        结果跟静态代理一样

        实现动态代理的方式实现代理的接口重写代理的方法。

        重写InvocationHandler接口下面的invoke方法  Object invoke(Object proxy, Method method, Object[] args)

        参数 proxy  调用方法的实例

          method 对应于代理实例上调用接口的方法的Method实例

          args 方法需要的参数

        

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

        参数 loader 类加载器

          interfaces 接口

          h 动态代理

        newProxyInstance底层代码  主要代码不包含验证等代码

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            // 返回一个代理类  用到了getProxyClass0方法
            Class<?> cl = getProxyClass0(loader, intfs);
            // 得到构造函数
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            // 返回代理的对象
            return cons.newInstance(new Object[]{h});
           
        }

        getProxyClass0 底层代码

    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);
        }

        get 底层代码 省略一些缓存操作

      

     1 public V get(K key, P parameter) {
     2             Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
     3             Supplier<V> supplier = valuesMap.get(subKey);
     4             WeakCache.Factory factory = null;
     5 
     6             while (true) {
     7                 if (supplier != null) {
     8                     // supplier might be a Factory or a CacheValue<V> instance
     9                     V value = supplier.get();
    10                     if (value != null) {
    11                         return value;
    12                     }
    13                 }
    14                 // else no supplier in cache
    15                 // or a supplier that returned null (could be a cleared CacheValue
    16                 // or a Factory that wasn't successful in installing the CacheValue)
    17 
    18                 // lazily construct a Factory
    19                 if (factory == null) {
    20                     factory = new WeakCache.Factory(key, parameter, subKey, valuesMap);
    21                 }
    22 
    23                 if (supplier == null) {
    24                     supplier = valuesMap.putIfAbsent(subKey, factory);
    25                     if (supplier == null) {
    26                         // successfully installed Factory
    27                         supplier = factory;
    28                     }
    29                     // else retry with winning supplier
    30                 } else {
    31                     if (valuesMap.replace(subKey, supplier, factory)) {
    32                         // successfully replaced
    33                         // cleared CacheEntry / unsuccessful Factory
    34                         // with our Factory
    35                         supplier = factory;
    36                     } else {
    37                         // retry with current supplier
    38                         supplier = valuesMap.get(subKey);
    39                     }
    40                 }
    41             }
    42         }

        第六行进入while循环   第七行到第十三行 表示找到并有数值 跳出循环 

        如果是新增的 就进入循环实例化工厂 把类加载器和接口保存在valuesMap中 然后在进行循环等等

        第二行  subKeyFactory.apply(key, parameter)  

        subKeyFactory 是 private final BiFunction<K, P, V> valueFactory; 这么定义的   是BiFunction接口的实例

        Proxy中静态方法ProxyClassFactory实现了这个接口    所以subKeyFactory.apply(key, parameter)  这个方法是ProxyClassFactory中的apply方法

     1 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
     2 
     3             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
     4             for (Class<?> intf : interfaces) {
     5                 /*
     6                  * Verify that the class loader resolves the name of this
     7                  * interface to the same Class object.
     8                  */
     9                 Class<?> interfaceClass = null;
    10                 try {
    11                     interfaceClass = Class.forName(intf.getName(), false, loader);
    12                 } catch (ClassNotFoundException e) {
    13                 }
    14                 if (interfaceClass != intf) {
    15                     throw new IllegalArgumentException(
    16                         intf + " is not visible from class loader");
    17                 }
    18                 /*
    19                  * Verify that the Class object actually represents an
    20                  * interface.
    21                  */
    22                 if (!interfaceClass.isInterface()) {
    23                     throw new IllegalArgumentException(
    24                         interfaceClass.getName() + " is not an interface");
    25                 }
    26                 /*
    27                  * Verify that this interface is not a duplicate.
    28                  */
    29                 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
    30                     throw new IllegalArgumentException(
    31                         "repeated interface: " + interfaceClass.getName());
    32                 }
    33             }
    34 
    35             String proxyPkg = null;     // package to define proxy class in
    36             int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    37 
    38             /*
    39              * Record the package of a non-public proxy interface so that the
    40              * proxy class will be defined in the same package.  Verify that
    41              * all non-public proxy interfaces are in the same package.
    42              */
    43             for (Class<?> intf : interfaces) {
    44                 int flags = intf.getModifiers();
    45                 if (!Modifier.isPublic(flags)) {
    46                     accessFlags = Modifier.FINAL;
    47                     String name = intf.getName();
    48                     int n = name.lastIndexOf('.');
    49                     String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
    50                     if (proxyPkg == null) {
    51                         proxyPkg = pkg;
    52                     } else if (!pkg.equals(proxyPkg)) {
    53                         throw new IllegalArgumentException(
    54                             "non-public interfaces from different packages");
    55                     }
    56                 }
    57             }
    58 
    59             if (proxyPkg == null) {
    60                 // if no non-public proxy interfaces, use com.sun.proxy package
    61                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    62             }
    63 
    64             /*
    65              * Choose a name for the proxy class to generate.
    66              */
    67             long num = nextUniqueNumber.getAndIncrement();
    68             String proxyName = proxyPkg + proxyClassNamePrefix + num;
    69 
    70             /*
    71              * Generate the specified proxy class.
    72              */
    73             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    74                 proxyName, interfaces, accessFlags);
    75             try {
    76                 return defineClass0(loader, proxyName,
    77                                     proxyClassFile, 0, proxyClassFile.length);
    78             } catch (ClassFormatError e) {
    79                 /*
    80                  * A ClassFormatError here means that (barring bugs in the
    81                  * proxy class generation code) there was some other
    82                  * invalid aspect of the arguments supplied to the proxy
    83                  * class creation (such as virtual machine limitations
    84                  * exceeded).
    85                  */
    86                 throw new IllegalArgumentException(e.toString());
    87             }
    88         }

        代码73行生成了代理类的class文件,76行返回来代理类对象

        

        执行main方法时候加上一句

    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        会生成一个$Proxy0.class文件

     1 final class $Proxy0 extends Proxy implements Person {
     2     private static Method m1;
     3     private static Method m2;
     4     private static Method m3;
     5     private static Method m0;
     6 
     7     public $Proxy0(InvocationHandler var1) throws  {
     8         super(var1);
     9     }
    10 
    11     public final boolean equals(Object var1) throws  {
    12         try {
    13             return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    14         } catch (RuntimeException | Error var3) {
    15             throw var3;
    16         } catch (Throwable var4) {
    17             throw new UndeclaredThrowableException(var4);
    18         }
    19     }
    20 
    21     public final String toString() throws  {
    22         try {
    23             return (String)super.h.invoke(this, m2, (Object[])null);
    24         } catch (RuntimeException | Error var2) {
    25             throw var2;
    26         } catch (Throwable var3) {
    27             throw new UndeclaredThrowableException(var3);
    28         }
    29     }
    30 
    31     public final void buyTicket() throws  {
    32         try {
    33             super.h.invoke(this, m3, (Object[])null);
    34         } catch (RuntimeException | Error var2) {
    35             throw var2;
    36         } catch (Throwable var3) {
    37             throw new UndeclaredThrowableException(var3);
    38         }
    39     }
    40 
    41     public final int hashCode() throws  {
    42         try {
    43             return (Integer)super.h.invoke(this, m0, (Object[])null);
    44         } catch (RuntimeException | Error var2) {
    45             throw var2;
    46         } catch (Throwable var3) {
    47             throw new UndeclaredThrowableException(var3);
    48         }
    49     }
    50 
    51     static {
    52         try {
    53             m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
    54             m2 = Class.forName("java.lang.Object").getMethod("toString");
    55             m3 = Class.forName("com.xuetang9.t11.aop.DynamicProxy$Person").getMethod("buyTicket");
    56             m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    57         } catch (NoSuchMethodException var2) {
    58             throw new NoSuchMethodError(var2.getMessage());
    59         } catch (ClassNotFoundException var3) {
    60             throw new NoClassDefFoundError(var3.getMessage());
    61         }
    62     }
    63 }

        class文件包括一个静态代码块51-61行  加载的时候先执行一些基本方法如equals toString 等

        31-39代表了动态代理的方法

        总结:动态代理应用于好多方面,如spring中aop,希望对以后阅读源码有帮助。

  • 相关阅读:
    【读书笔记】简约至上交互设计四策略目录
    Cassandra在Windows上安装及使用方法[转]
    [转]揭秘全球最大网站Facebook背后的那些软件
    过程改进计划
    制定项目管理计划
    在sublime text3中利用markdown
    ubuntu下更改用户名和主机名
    国庆有感
    最近两天学到的技术汇总
    看见了就当没有看见
  • 原文地址:https://www.cnblogs.com/liljoker/p/13024896.html
Copyright © 2020-2023  润新知