JAVA中的代理模式

代理模式

1. 什么是代理模式

  • 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用;类比即为生活中的中介,用图示表明:

在这里插入图片描述

2. 代理模式的作用

  • 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
  • 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

3. 代理模式中的三个角色

  • 代理模式中一般存在三个角色:
    在这里插入图片描述

    • 如图中所示:
      1. 抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
      2. 真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。本质上就是真正实现业务逻辑的对象
      3. 代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理!

4. 代理模式的两种方式

静态代理
  • 什么是静态代理:

    • 静态代理即我们需要手动去创建代理类,在程序运行之前,代理类的.class文件就已经被创建了
  • 静态代理的优缺点:

    • 优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展
    • 缺点一个代理类只能代理一个接口,如果存在多个服务接口,那么就得为每一个服务接口都创建一个代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改
  • 静态代理的使用:

    1. 创建抽象角色,即抽象接口
    2. 创建真实角色,即继承抽象接口并实现
    3. 创建代理角色,即包含真实角色,提供调用方式
    //1. 创建抽象角色
    package com.example.myclass;
    
    public interface fruit {
        public void apple();
    }
    
    //2. 创建真实角色
    package com.example.myclass.statics;
    
    import com.example.myclass.fruit;
    
    public class liFruit implements fruit {
        @Override
        public void apple() {
            System.out.println("新鲜苹果,包甜");
        }
    }
    
    //3. 创建代理角色
    package com.example.myclass.agnet;
    
    import com.example.myclass.fruit;
    import com.example.myclass.statics.liFruit;
    
    public class fruitAgent implements fruit {
    
        private liFruit li;
    
        public fruitAgent(liFruit a) {
            this.li = a;
        }
    
        private void before() {
            System.out.println("骑车取货送货");
        }
    
        private void after() {
            System.out.println("售后服务");
        }
    
        @Override
        public void apple() {
            this.before();
            this.li.apple();
            this.after();
        }
    }
    
    
    //实际使用举例
    public void main() {
            liFruit li = new liFruit();
            fruitAgent firuteagent =  new fruitAgent(li);
    
            firuteagent.apple();
    }
    
动态代理
  • 什么是动态代理

    • 动态代理是程序运行时通过反射机制动态创建代理对象
  • 动态代理的优缺点

    • 优点:相对于静态代理,极大的减少类的数量,降低工作量,减少对业务接口的依赖,降低耦合,便于后期维护;同时在某些情况下是最大的优势,即可以统一修改代理类的方法逻辑,而不需要像静态代理需要修改每个代理类
    • 缺点:因为使用的是反射,所以在运行时会消耗一定的性能;同时JDK代理只支持interface的动态代理,如果你再继续深究源码,会发现,所有动态生成的代理对象都有一个共同的父类,即都继承于Proxy;Java的单继承机制决定了无法支持class的动态代理,也就意味着你拿到动态生成的代理对象,只能调用其实现的接口里的方法,无法像静态代理中的代理类可以在内部扩展更多的功能
  • 动态代理的使用

    1. 调用Proxy类进行创建一个对象O

    2. Proxy对象O中的invoke函数中进行对应逻辑的实现

    3. 调用O对象进行调用方法

      //接口
      package com.example.myclass;
      
      public interface fruit {
          public void apple();
      }
      
      //真实角色
      package com.example.myclass.statics;
      
      import com.example.myclass.fruit;
      
      public class liFruit implements fruit {
          @Override
          public void apple() {
              System.out.println("新鲜苹果,包甜");
          }
      }
      
      //动态代理方式
      public void main() {
          final liFruit liFruit = new liFruit();
          Object o = Proxy.newProxyInstance(ExampleUnitTest.class.getClassLoader(),
                  new Class[]{fruit.class, vegatable.class}, new InvocationHandler() {
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          method.invoke(liFruit, args);
                          return null;
                      }
                  });
      
          //只能通过接口去继承转换
          fruit f = (fruit) o;
          f.apple();
      }
      

    5. 动态代理的实现原理

    • 通过上面动态代理的实现,发现是通过 newProxyInstance() 函数去实现的,查看SDK中的代码实现

      @CallerSensitive
          public static Object newProxyInstance(ClassLoader loader,
                                                Class<?>[] interfaces,
                                                InvocationHandler h)
              throws IllegalArgumentException
          {
              Objects.requireNonNull(h);
      
              //对接口进行拷贝,获取所有的接口
              final Class<?>[] intfs = interfaces.clone();
              // Android-removed: SecurityManager calls
              /*
              final SecurityManager sm = System.getSecurityManager();
              if (sm != null) {
                  checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
              }
              */
      
              /*
               * Look up or generate the designated proxy class.
               */
              //通过拷贝出来的接口,拿到一个Class对象
              Class<?> cl = getProxyClass0(loader, intfs);
      
              /*
               * Invoke its constructor with the designated invocation handler.
               */
              try {
                  // Android-removed: SecurityManager / permission checks.
                  /*
                  if (sm != null) {
                      checkNewProxyPermission(Reflection.getCallerClass(), cl);
                  }
                  */
      
                  //通过反射去获取这个Class对象的构造器
                  final Constructor<?> cons = cl.getConstructor(constructorParams);
                  final InvocationHandler ih = h;
                  if (!Modifier.isPublic(cl.getModifiers())) {
                      // BEGIN Android-removed: Excluded AccessController.doPrivileged call.
                      /*
                      AccessController.doPrivileged(new PrivilegedAction<Void>() {
                          public Void run() {
                              cons.setAccessible(true);
                              return null;
                          }
                      });
                      */
      
                      cons.setAccessible(true);
                      // END Android-removed: Excluded AccessController.doPrivileged call.
                  }
                  //通过反射获得的构造器实例化出一个实例对象
                  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);
              }
          }
      

      由上面的newProxyInstance()函数分析可以看出,本质上是通过这个接口去构建出对应接口的一个实例化对象;而常规的操作中,一个类的完整生命周期分为以下几个步骤: JAVA源文件(JAVA文件)–/编译/–>Java字节码(.class文件)–/类加载/–>Class对象–/实例化/–>实例对象---->卸载;

      • class对象的来源(.class文件)
        • 在正常编译流程中,编译出来的Java字节码是以<.class>文件存储在硬盘中的,然后再通过类加载器根据JAVA字节码去构建出一个Class对象
        • 而动态代理过程中,是直接在内存中去生成的,而不存在一个.class文件

      那么问题又来了,动态代理的class对象是怎么在内存中生成的?这个生成的Class对象又是什么样子的呢?

    • 动态代理的class对象是怎么生成的

      • 首先从上面的newProxyInstance()中我们可以看到,class对象是通过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
            /*根据源码注释,当loader里存在接口实现时,就直接返回loader拷贝;否则,就通过ProxyClassFactory方法去创建一个代理类*/
            return proxyClassCache.get(loader, interfaces);
        }
        
      • 本质上,是通过proxyClassCache去获得的,那么proxyClassCache又是什么呢?查看JDK中Proxy类的实现

        private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache(new Proxy.KeyFactory(), new Proxy.ProxyClassFactory());
        
      • 排查后发现,是通过ProxyClassFactory()函数进行获取的

        private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
            private static final String proxyClassNamePrefix = "$Proxy";
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
        
            private ProxyClassFactory() {
            }
        
            public Class<?> apply(ClassLoader var1, Class<?>[] var2) {
                IdentityHashMap var3 = new IdentityHashMap(var2.length);
                Class[] var4 = var2;
                int var5 = var2.length;
        
                for(int var6 = 0; var6 < var5; ++var6) {
                    Class var7 = var4[var6];
                    Class var8 = null;
        
                    try {
                        var8 = Class.forName(var7.getName(), false, var1);
                    } catch (ClassNotFoundException var15) {
                    }
        
                    if (var8 != var7) {
                         throw new IllegalArgumentException(var7 + " is not visible from class loader");
                     }
        
                     //这里会判断动态代理是不是接口,如果不是接口,会在这里直接抛异常
                     if (!var8.isInterface()) {
                         throw new IllegalArgumentException(var8.getName() + " is not an interface");
                     }
        
                     if (var3.put(var8, Boolean.TRUE) != null) {
                         throw new IllegalArgumentException("repeated interface: " + var8.getName());
                     }
                 }
        
                 String var16 = null;
                 byte var17 = 17;
                 Class[] var18 = var2;
                 int var20 = var2.length;
        
                 for(int var21 = 0; var21 < var20; ++var21) {
                     Class var9 = var18[var21];
                     int var10 = var9.getModifiers();
                     if (!Modifier.isPublic(var10)) {
                        var17 = 16;
                        String var11 = var9.getName();
                        int var12 = var11.lastIndexOf(46);
                        String var13 = var12 == -1 ? "" : var11.substring(0, var12 + 1);
                        if (var16 == null) {
                            var16 = var13;
                        } else if (!var13.equals(var16)) {
                             throw new IllegalArgumentException("non-public interfaces from different packages");
                        }
                    }
                 }
        
                 if (var16 == null) {
                     var16 = "com.sun.proxy.";
                 }
        
                 long var19 = nextUniqueNumber.getAndIncrement();
                 String var23 = var16 + "$Proxy" + var19;
                //最终就是通过这个函数进行JAVA字节码生成的,可以看到最终生成的是一个byte[]数组
                 byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);
        
                 try {
                     return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
                 } catch (ClassFormatError var14) {
                     throw new IllegalArgumentException(var14.toString());
                 }
            }
        }
        
      • 解析generateProxyClass()函数

        public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
                ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
                final byte[] var4 = var3.generateClassFile();
                if (saveGeneratedFiles) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            try {
                                int var1 = var0.lastIndexOf(46);
                                Path var2;
                                if (var1 > 0) {
                                    Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                                    Files.createDirectories(var3);
                                    var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                                } else {
                                    var2 = Paths.get(var0 + ".class");
                                }
        
                                Files.write(var2, var4, new OpenOption[0]);
                                return null;
                            } catch (IOException var4x) {
                                throw new InternalError("I/O exception saving generated file: " + var4x);
                            }
                        }
                    });
                }
        
                return var4;
            }
        
      • 至此,后续代码没有必要再看了,我们可以得知,最终的class对象是通过generateProxyClass()函数获取的

    • 动态代理的class对象中的代码到底是什么样的

      • 由上面的动态代理的class对象是怎么生成的探究,我们得知可以通过generateProxyClass()函数获取的,那么我们可以调用该函数,获取该class对象,将其保存到真实文件中来查看

        //使用FileOutputStream,将内存中构建的类输出到文件中
        //1. 创建抽象角色
        package com.example.lib;
        
        public interface test {
            public void tt();
        }
        
        //2. 将动态代理在内存中的类输出到文件中来
        //要使用ProxyGenerator.generateProxyClass方法,需要导入这个包
        import sun.misc.ProxyGenerator;
        public class MyClass {
            public static void main(String[] args) throws Exception {
                proxy();
            }
        
            private static void proxy() throws IOException {
                String name = test.class.getName() + "$Proxy0";
                //生成代理指定接口的Class数据
                byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{test.class});
                FileOutputStream fos = new FileOutputStream("lib/" + name + ".class");
                fos.write(bytes);
                fos.close();
            }
        }
        
        
        • 通过这个方式,将内存中创建的class类完整的导入到文件中,此时我们可以打开这个类,看一下其内部的代码
        //
        // Source code recreated from a .class file by IntelliJ IDEA
        // (powered by Fernflower decompiler)
        //
        
        package com.example.lib;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
        import java.lang.reflect.UndeclaredThrowableException;
        
        public final class test$Proxy0 extends Proxy implements test {
            private static Method m1;
            private static Method m2;
            private static Method m3;
            private static Method m0;
        
            public test$Proxy0(InvocationHandler var1) throws  {
                //构造方法中,调用了super(var1)
                //其中var1就是我们的InvocationHandler,而super其实就是Proxy,进入查看
                /**
                *	protected Proxy(InvocationHandler var1) {
                * 		Objects.requireNonNull(var1);
                *		this.h = var1;
            	*	}
                */
                //在Proxy的构造函数中,我们将InvocationHandler赋值到了this.h上
                super(var1);
            }
        
            public final boolean equals(Object var1) throws  {
                try {
                    return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
                } catch (RuntimeException | Error var3) {
                    throw var3;
                } catch (Throwable var4) {
                    throw new UndeclaredThrowableException(var4);
                }
            }
        
            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);
                }
            }
        
            public final void tt() throws  {
                try {
                    //由上面的分析,我们可以知道,h其实就是InvocationHandler,所以,当我们调用
                    //接口中的tt()方法,本质上我们就是调用了InvocationHandler的invoke方法,
                    //即回调到了newProxyInstance中的invoke()方法
                    //而这个m3,看下面的静态代码块
                    super.h.invoke(this, m3, (Object[])null);
                } catch (RuntimeException | Error var2) {
                    throw var2;
                } catch (Throwable var3) {
                    throw new UndeclaredThrowableException(var3);
                }
            }
        
            public final int hashCode() throws  {
                try {
                    return (Integer)super.h.invoke(this, m0, (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"));
                    m2 = Class.forName("java.lang.Object").getMethod("toString");
                    //从这里我们可以看到,m3实际上就是通过反射获得的test接口中的tt方法
                    m3 = Class.forName("com.example.lib.test").getMethod("tt");
                    m0 = Class.forName("java.lang.Object").getMethod("hashCode");
                } catch (NoSuchMethodException var2) {
                    throw new NoSuchMethodError(var2.getMessage());
                } catch (ClassNotFoundException var3) {
                    throw new NoClassDefFoundError(var3.getMessage());
                }
            }
        }
        
        • 由此,我们动态代理的原理探究也就告一段落了,在函数实现的时候,需要注意:

          • import sun.misc.ProxyGenerator; 必须是在java模块中进行导入,查看Android SDK中的方法

                /**
                 * A factory function that generates, defines and returns the proxy class given
                 * the ClassLoader and array of interfaces.
                 */
                private static final class ProxyClassFactory
                    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
                {
                    // prefix for all proxy class names
                    private static final String proxyClassNamePrefix = "$Proxy";
            
                    // next number to use for generation of unique proxy class names
                    private static final AtomicLong nextUniqueNumber = new AtomicLong();
            
                    @Override
                    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 the default package.
                            proxyPkg = "";
                        }
            
                        {
                            //此处的说明表示Android SDK中的实现和JDK中的是不同的
                            // Android-changed: Generate the proxy directly instead of calling
                            // through to ProxyGenerator.
                            List<Method> methods = getMethods(interfaces);
                            Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                            validateReturnTypes(methods);
                            List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
            
                            Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                            Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
            
                            /*
                             * Choose a name for the proxy class to generate.
                             */
                            long num = nextUniqueNumber.getAndIncrement();
                            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            
                            //SDK中用的是该方式,而非JDK中的ProxyGenerator.generateProxyClass()方法
                            return generateProxy(proxyName, interfaces, loader, methodsArray,
                                                 exceptionsArray);
                        }
                    }
                }
            

            而这也导致了,在Android模块中,我们无法使用ProxyGenerator.generateProxyClass()方法,且import sun.misc.ProxyGenerator;该包也无法在Android中正常导入

          • 使用Android studio,还需要进行如下设置,否则也无法编译通过,原因还是在于import sun.misc.ProxyGenerator;包的导入

          在这里插入图片描述

6. 优秀博客:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值