动态代理

jdk源码

Posted by Cc1over on November 14, 2018

动态代理

Retrofit2关键源码

 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

前四行其实就是对传进来的Class对象进行校验,判断它是否是接口或者是否继承其他接口,然后接下来其实就是生成一个动态代对象,它里面有持有一个Platform对象的引用,其实就是用表面当前的平台,而Android和Java8是区别对待的,而关于java8中的一段代码

   @Override Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object,
        @Nullable Object... args) throws Throwable {
      // Because the service interface might not be public, we need to use a MethodHandle lookup
      // that ignores the visibility of the declaringClass.
      Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class, int.class);
      constructor.setAccessible(true);
      return constructor.newInstance(declaringClass, -1 /* trusted */)
          .unreflectSpecial(method, declaringClass)
          .bindTo(object)
          .invokeWithArguments(args);
    }

这里主要是为了解决:如何接口中有默认方法,就不代理默认方法。
然后继续回到 ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);这一行代码

ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

这里其实只是很简单的用了ConcurrentHashMap对ServiceMethod对象进行缓存,但这里有一个小细节就是为什么这里要用synchronized关键字,我也没想懂,但是我又联想到一些东西:在JDK1.6中ConcurrentHashMap使用锁分段技术提高并发访问效率。首先将数据分成一段一段地存储,然后给每一段数据配一个锁,当一个线程占用锁访问其中一段数据时,其他段的数据也能被其他线程访问。然而在jdk1.8中的实现已经抛弃了Segment分段锁机制,利用CAS+Synchronized来保证并发更新的安全。
下面看的是ServiceMethod.Builder<>().build的代码

   callAdapter = createCallAdapter();
   responseType = callAdapter.responseType();
   responseConverter = createResponseConverter();
   .....
   for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
   }

在这里的逻辑主要就是获取几个接口对象,解析Method的注解,主要就是获取Http请求的方法,比如是GET还是POST还是其他形式,如果没有,程序就会报错,还会做一系列的检查,比如如果在方法上注解了@Multipart,但是Http请求方法是GET,同样也会报错。比如上面api中带有一个参数{user},这是一个占位符,而真实的参数值在Java方法中传入,那么Retrofit会使用一个ParameterHandler来进行替换,最后,ServiceMethod会做其他的检查,比如用了@FormUrlEncoded注解,那么方法参数中必须至少有一个@Field或@FieldMap。

小总结

  • OkHttpCall是实现了Call接口的,并且是真正调用OkHttp3发送Http请求的类。OkHttp3发送一个Http请求需要一个Request对象,而这个Request对象就是从ServiceMethod的toRequest返回的
  • 总的来说,OkHttpCall就是调用ServiceMethod获得一个可以执行的Request对象,然后等到Http请求返回后,再将response body传入ServiceMethod中,ServiceMethod就可以调用Converter接口将response body转成一个Java对象
  • 结合上面说的就可以看出,ServiceMethod中几乎保存了一个api请求所有需要的数据,OkHttpCall需要从ServiceMethod中获得一个Request对象,然后得到response后,还需要传入ServiceMethod用Converter转换成Java对象
  • Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求

JDK动态代理

    return (T) Proxy.newProxyInstance(real.getClass().getClassLoader(), real.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("jdkProxy");
                  return method.invoke(real, args);
        });

先看的是newProxyInstance方法(ps:把一些无关紧要的东西去掉了)


        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
           Class<?> cl = getProxyClass0(loader, intfs);
         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});

可以看到方法里面有两处检查Permission的地方以及AccessController,这与Java的安全模型有关,在这里就不细讲了,先看一下checkProxyAccess方法

private static void checkProxyAccess(Class<?> caller,
                                         ClassLoader loader,
                                         Class<?>... interfaces)
    {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader ccl = caller.getClassLoader();
            if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) {
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
            ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
        }
    }

这个方法主要用来检查是否有权限创建一个代理类,其中分别进行了classloader的验证以及接口的包权限,然后通过getProxyClass0去或者代理的class对象,然后再去检查需不需要弄个新的代理,就是获得是否创建代理类的权限,接下来就是获得构造器,检查字节码文件的修饰符,判断它是不是public,若不是就通过AccessController申请更大的权限。 然后我们下面看一下的是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);

这里就判断了一下interfaces的数量是否超过65535,然后下面从缓存中获取代理class,没有的话就去创建一个,接下来看看这个缓存类

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

这个ProxyClassFactory就是用于去创建代理class,里面的关键方法就是apply方法

 @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
         
                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");
                }
            
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
           
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;    
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
           
            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 + ".";
            }

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

            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

在上面的代码就可以看到的字节码的生成规则,首先反射加载指定的接口,接下来判断自己的classload加载的class与你传入的class是否相等,如果不相等就抛出异常,然后判断如果传入不是一个接口类型,就抛异常,然后验证接口是否重复,然后为代理的方法都添加上public和final的修饰符,然后检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是 interface 类型而不是 class 类型。也就是说,这一段是看你传入的接口中有没有不是public的接口,如果有,这些接口必须全部在一个包里定义的,否则抛异常,然后就生成代理类的类名,最后生成代理类的class文件, 返回字节流。实际上真正生成代理的类是在这里ProxyGenerator。 然后再回到上面的KeyFactory看看它做了神魔

  private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
    {
        @Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
            switch (interfaces.length) {
                case 1: return new Key1(interfaces[0]); // the most frequent
                case 2: return new Key2(interfaces[0], interfaces[1]);
                case 0: return key0;
                default: return new KeyX(interfaces);
            }
        }
    }

发现它只是根据接口的长度去返回对应的Key,这样有点不直观,接下来就进WeakCache中看一下在缓存中如何调用上述的两个工厂,先看一下的是在Proxy中调用的get方法

public V get(K key, P parameter) {
      //这里要求实现的接口不能为空
      Objects.requireNonNull(parameter);
      //清除过期的缓存
      expungeStaleEntries();
      //将ClassLoader包装成CacheKey, 作为一级缓存的key
      Object cacheKey = CacheKey.valueOf(key, refQueue);
      //获取得到二级缓存
      ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
     //如果根据ClassLoader没有获取到对应的值
     if (valuesMap == null) {
         //以CAS方式放入, 如果不存在则放入,否则返回原先的值
         ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, 
                 valuesMap = new ConcurrentHashMap<>());
         //如果oldValuesMap有值, 说明放入失败
         if (oldValuesMap != null) {
             valuesMap = oldValuesMap;
               }    
        }
     //根据代理类实现的接口数组来生成二级缓存key, 分为key0, key1, key2, keyx
     Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
     //这里通过subKey获取到二级缓存的值
     Supplier<V> supplier = valuesMap.get(subKey);
     Factory factory = null;
     //这个循环提供了轮询机制, 如果条件为假就继续重试直到条件为真为止
     while (true) {
         //如果通过subKey取出来的值不为空
         if (supplier != null) {
             //在这里supplier可能是一个Factory也可能会是一个CacheValue
             //在这里不作判断, 而是在Supplier实现类的get方法里面进行验证
             V value = supplier.get();
             if (value != null) {
                 return value;
             }
         }
         if (factory == null) {
             //新建一个Factory实例作为subKey对应的值
             factory = new Factory(key, parameter, subKey, valuesMap);
         }
         if (supplier == null) {
             //到这里表明subKey没有对应的值, 就将factory作为subKey的值放入
             supplier = valuesMap.putIfAbsent(subKey, factory);
             if (supplier == null) {
                 //到这里表明成功将factory放入缓存
                 supplier = factory;
             }
             //否则, 可能期间有其他线程修改了值, 那么就不再继续给subKey赋值, 而是取出来直接用
         } else {
             //期间可能其他线程修改了值, 那么就将原先的值替换
             if (valuesMap.replace(subKey, supplier, factory)) {
                 //成功将factory替换成新的值
                 supplier = factory;
             } else {
                 //替换失败, 继续使用原先的值
                 supplier = valuesMap.get(subKey);
             }
         }
     }
 }

WeakCache的get方法并没有用锁进行同步,那它是怎样实现线程安全的呢?因为它的所有会进行修改的成员变量都使用了ConcurrentMap,这个类是线程安全的。因此它将自身的线程安全委托给了ConcurrentMap, get方法尽可能的将同步代码块缩小,这样可以有效提高WeakCache的性能。我们看到ClassLoader作为了一级缓存的key,这样可以首先根据ClassLoader筛选一遍,因为不同ClassLoader加载的类是不同的。然后它用接口数组来生成二级缓存的key,这里它进行了一些优化,因为大部分类都是实现了一个或两个接口,所以二级缓存key分为key0,key1,key2,keyX。key0到key2分别表示实现了0到2个接口,keyX表示实现了3个或以上的接口,事实上大部分都只会用到key1和key2。这些key的生成工厂是在Proxy类中,通过WeakCache的构造器将key工厂传入。这里的二级缓存的值是一个Factory实例,最终代理类的值是通过Factory这个工厂来获得的。

   //Reference引用队列
    private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
    //缓存的底层实现, key为一级缓存, value为二级缓存。 为了支持null, map的key类型设置为Object
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> 
                                                         map = new ConcurrentHashMap<>();
    //reverseMap记录了所有代理类生成器是否可用, 这是为了实现缓存的过期机制
    private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
    //生成二级缓存key的工厂, 这里传入的是KeyFactory
    private final BiFunction<K, P, ?> subKeyFactory;
    //生成二级缓存value的工厂, 这里传入的是ProxyClassFactory
    private final BiFunction<K, P, V> valueFactory;
 
   //构造器, 传入生成二级缓存key的工厂和生成二级缓存value的工厂
  public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) {
      this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
     this.valueFactory = Objects.requireNonNull(valueFactory);
 }

我们看一下WeakCache的成员变量和构造器,WeakCache缓存的内部实现是通过ConcurrentMap来完成的,成员变量map就是二级缓存的底层实现,reverseMap是为了实现缓存的过期机制,subKeyFactory是二级缓存key的生成工厂,通过构造器传入,这里传入的值是Proxy类的KeyFactory,valueFactory是二级缓存value的生成工厂,通过构造器传入,这里传入的是Proxy类的ProxyClassFactory。

 private final class Factory implements Supplier<V> {
      //一级缓存key, 根据ClassLoader生成
      private final K key;
      //代理类实现的接口数组
      private final P parameter;
      //二级缓存key, 根据接口数组生成
      private final Object subKey;
      //二级缓存
      private final ConcurrentMap<Object, Supplier<V>> valuesMap;
 
      Factory(K key, P parameter, Object subKey,
             ConcurrentMap<Object, Supplier<V>> valuesMap) {
          this.key = key;
          this.parameter = parameter;
          this.subKey = subKey;
          this.valuesMap = valuesMap;
     }
 
      @Override
      public synchronized V get() {
          //这里再一次去二级缓存里面获取Supplier, 用来验证是否是Factory本身
          Supplier<V> supplier = valuesMap.get(subKey);
          if (supplier != this) {
              //在这里验证supplier是否是Factory实例本身, 如果不则返回null让调用者继续轮询重试
              //期间supplier可能替换成了CacheValue, 或者由于生成代理类失败被从二级缓存中移除了
              return null;
          }
          V value = null;
          try {
              //委托valueFactory去生成代理类, 这里会通过传入的ProxyClassFactory去生成代理类
              value = Objects.requireNonNull(valueFactory.apply(key, parameter));
          } finally {
              //如果生成代理类失败, 就将这个二级缓存删除
              if (value == null) {
                  valuesMap.remove(subKey, this);
              }
          }
          //只有value的值不为空才能到达这里
          assert value != null;
          //使用弱引用包装生成的代理类
          CacheValue<V> cacheValue = new CacheValue<>(value);
          //将包装后的cacheValue放入二级缓存中, 这个操作必须成功, 否则就报错
          if (valuesMap.replace(subKey, this, cacheValue)) {
              //将cacheValue成功放入二级缓存后, 再对它进行标记
              reverseMap.put(cacheValue, Boolean.TRUE);
          } else {
              throw new AssertionError("Should not reach here");
          }
          //最后返回没有被弱引用包装的代理类
          return value;
      }
 }
复制代码

我们再看看Factory这个内部工厂类,可以看到它的get方法是使用synchronized关键字进行了同步。进行get方法后首先会去验证subKey对应的suppiler是否是工厂本身,如果不是就返回null,而WeakCache的get方法会继续进行重试。如果确实是工厂本身,那么就会委托ProxyClassFactory生成代理类,ProxyClassFactory是在构造WeakCache的时候传入的。所以这里解释了为什么最后会调用到Proxy的ProxyClassFactory这个内部工厂来生成代理类。生成代理类后使用弱引用进行包装并放入reverseMap中,最后会返回原装的代理类。

小总结

抛开JDK动态代理中的缓存机制,安全机制,JDK动态代理就是针对接口实现的。

参考资料:

  • https://www.cnblogs.com/liuyun1995/p/8144676.html
  • https://www.jianshu.com/p/c1a3a881a144