Nitro's

May 18, 2019 - Comments - tech

Java动态代理类的生成和调用

Java的动态代理在开发中都有大量的使用,如Spring框架,Android的HTTP开发框架Retrofit,本文主要关注动态代理编写使用背后的代理类是如何的生成以及与代理对象的调用是如何实现的,以Retrofit这个框架中动态代理的使用为例子进行分析。

在Retrofit中,某类的API接口大多定义在一个Service接口类中,某一个API接口则被声明为Service接口类的一个方法,通过方法的注解、参数来描述具体API参数等信息。在调用某个API接口时,Retrofit实例调用create()创建Service接口类的实例对象来完成调用,具体源代码如下:

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.callAdapter.adapt(okHttpCall);  
       }  
     });  
}

源代码

假设我们创建了一个用户列表的API接口,则Service类定义如下:

public interface UserService {  
  
   @GET("user/list")  
   Call<List<User>> userList();  
}  

此时如果调用Retrofit的onCreate(UserService.class)时将创建UserService接口的实例,如果我们要userList()调用前后进行其他的操纵行为,就可以在代理类的invoke()调用中施加行为,那我们的被代理对象的调用过程只是invoke中通过反射回调就完成了吗?这里面是怎么实现的呢?带着疑问通过源代码去分析一下这个过程。

使用动态代理一般是通过Proxy的newProxyInstance()来创建代理类,这个方法有三个参数:

ClassLoader指定要加载的接口的classLoader  

Class<?>[] 指定要加载的接口类  

InvocationHandler 指定代理回调的处理

这个方法的核心代码包含两部分:

//找到代理类的Class定义
Class<?> cl = getProxyClass0(loader, intfs);
 
//构造出代理类的实现并返回
return cons.newInstance(new Object[]{h});

通过以上两步,我们就可以拿到一个代理类的实现,从而实现调用某个接口在invoke()中让被代理对象得以调用。这里的问题就变成这个代理类Proxy是怎么找到的?而且它还将InvocationHandler类插入到了构造函数中才实现了我们的被代理过程?核心就在于上面的getProxyClass0方法。getProxyClass0()的核心只有一个,从proxyClassCache中获取代理类的定义,那问题就变成这个proxyClassCache是怎么得到的,里面包含了什么?

proxyClassCache是WeakCache的声明,这个类和Collection集的Map很像,实现了KV存取,但它包含sub-key,而且key、sub-key、value之间存在变换关系,这种变换是通过构造函数的两个工厂类实现的subKeyFactoryvalueFactory。还是直奔目的地,直接去看getProxyClass0方法中proxyClassCache的get(),在这个方法中我们先忽略关于前面key、sub-key的转换,关注核心的value中代理类的存取,代理类的返回只有位置:

while (true) {  
   if (supplier != null) {  
       // supplier might be a Factory or a CacheValue<V> instance  
       V value = supplier.get();  
       if (value != null) {  
           return value;  
       }  
   }  
   // else no supplier in cache  
   // or a supplier that returned null (could be a cleared CacheValue  
   // or a Factory that wasn't successful in installing the CacheValue)  
  
   // lazily construct a Factory  
   if (factory == null) {  
       factory = new Factory(key, parameter, subKey, valuesMap);  
   }  
  
   if (supplier == null) {  
       supplier = valuesMap.putIfAbsent(subKey, factory);  
       if (supplier == null) {  
           // successfully installed Factory  
           supplier = factory;  
       }  
       // else retry with winning supplier  
   } else {  
       if (valuesMap.replace(subKey, supplier, factory)) {  
           // successfully replaced  
           // cleared CacheEntry / unsuccessful Factory  
           // with our Factory  
           supplier = factory;  
       } else {  
           // retry with current supplier  
           supplier = valuesMap.get(subKey);  
       }  
   }  
}

如果supplier不为空就去通过get()获取,如果为空则继续后面的执行,这个方法是一个无限循环,也就是后续必须保证获取到supplier,所以继续往下看会发现这个supplier其实就是WeakCache.Factory类,通过调用Factory的get()获取代理类的定义。

@Override  
public synchronized V get() { // serialize access  
   // re-check  
   Supplier<V> supplier = valuesMap.get(subKey);  
   if (supplier != this) {  
       // something changed while we were waiting:  
       // might be that we were replaced by a CacheValue  
       // or were removed because of failure ->  
       // return null to signal WeakCache.get() to retry  
       // the loop  
       return null;  
   }  
   // else still us (supplier == this)  
  
   // create new value  
   V value = null;  
   try {  
       value = Objects.requireNonNull(valueFactory.apply(key, parameter));  
   } finally {  
       if (value == null) { // remove us on failure  
           valuesMap.remove(subKey, this);  
       }  
   }  
   // the only path to reach here is with non-null value  
   assert value != null;  
  
   // wrap value with CacheValue (WeakReference)  
   CacheValue<V> cacheValue = new CacheValue<>(value);  
  
   // put into reverseMap  
   reverseMap.put(cacheValue, Boolean.TRUE);  
  
   // try replacing us with CacheValue (this should always succeed)  
   if (!valuesMap.replace(subKey, this, cacheValue)) {  
       throw new AssertionError("Should not reach here");  
   }  
  
   // successfully replaced us with new CacheValue -> return the value  
   // wrapped by it  
   return value;  
}

这其中也包含了一些缓存的处理判断,我们默认缓存获取不到,继续往下看,就能发现value的返回来自于valueFactory的apply(),这在我们刚刚的WeakCache类构造方法中提到过。在WeakCache中走了一圈缓存的判断,最后又回到构造WeakCache的地方Proxy类:

/*
 * a cache of proxy classes  
*/  
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
   proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

它的第二个参数是ProxyClassFactory,所以我们继续跟下去看这个类的apply做了什么?

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {  
  
           // 前面主要关注一些权限检查、是否为interface类的检查  
  
       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.PROXYPACKAGE + ".";  
       }  
  
       /*  
        * 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());  
       }  
   }  
}

在apply()中有发现重点,因为我们看到了类名的拼装,或许这就是那个代理类,事实证明它就在这里了。

首先如果接口类的的权限非public那就用接口类的包名,其他统一为com.sun.proxy,这是包名的定义,类名为$Proxy,然后一个全局自增的数字,类似于匿名内部类类名的生成。这样代理类的包名、类名就完整了,后面的重点就是这个代理类是如何将我们的接口类定义放到这个代理类中,这个代理类又是如何把InvokeHandler的回调抛给代理对象执行的。这两个核心问题的解决来自于下面两个方法:

ProxyGenerator.generateProxyClass()
 
defineClass0()

这两个方法,一个rt.jar包中的非开源方法,一个是native方法,后面这个方法的核心就是将我们生成的Java字节码文件的字节数组加载然后返回给我们一个代理类的Class以供我们前面描述的构造实例使用,所以我们就先通过反编译看一下generateProxyClass()都做了什么事情。

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

这方法的内部调用了私有的generateClassFile()来生成类的字节码的字节数组,然后将字节码的class文件保存在包名定义的路径下。

generateClassFile()中重点是通过ProxyGenerator.ProxyMethod类来生成对接口类的方法的实现以及被代理对象的Object核心方法,比如equal、hashcode这些方法,因为这些类可能会特殊的实现,必须要保证正确的代理,就需要保证是来自于被代理对象的实现。

this.addProxyMethod(hashCodeMethod, Object.class);  
this.addProxyMethod(equalsMethod, Object.class);  
this.addProxyMethod(toStringMethod, Object.class);  
Class[] var1 = this.interfaces;
 
  ......
  
  while(var15.hasNext()) {  
   ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();  
   this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));  
   this.methods.add(var16.generateMethod());  
}
  
  ......
  
  while(var15.hasNext()) {  
   ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();  
   var20.write(var14);  
}  
  
var14.writeShort(this.methods.size());  
var15 = this.methods.iterator();  
  
while(var15.hasNext()) {  
   ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();  
   var21.write(var14);  
}

通过对字节码级别的类、方法、成员变量等元素的生成,我们将会看到如下的类,当然如果需要看到这个class文件还需要添加一个properties属性。

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

假设定义的接口类文件如下:

package com.nitrohsu;  
  
public interface ITable {  
   int open();  
}

那么生成的代理类class文件如下

package com.sun.proxy;  
  
import com.nitrohsu.ITable;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
import java.lang.reflect.UndeclaredThrowableException;  
  
public final class $Proxy0 extends Proxy implements ITable {  
   private static Method m1;  
   private static Method m2;  
   private static Method m3;  
   private static Method m0;  
  
   public $Proxy0(InvocationHandler var1) throws  {  
       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 int open() throws  {  
       try {  
           return (Integer)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 = Class.forName("com.nitrohsu.ITable").getMethod("open");  
           m0 = Class.forName("java.lang.Object").getMethod("hashCode");  
       } catch (NoSuchMethodException var2) {  
           throw new NoSuchMethodError(var2.getMessage());  
       } catch (ClassNotFoundException var3) {  
           throw new NoClassDefFoundError(var3.getMessage());  
       }  
   }  
}  

我们可以清楚的看到这个代理类会将接口类实现,而且通过构造方法将InvocationHandler传入,在调用代理类调用任意一个接口时通过InvocationHandler的invoke方法将代理的回调抛出到外部,以供被代理对象来实现操纵或调用,这里也解释了为什么Proxy类的newProxyInstance()中代理类的构造方法反射为什么会有一个包含了InvocationHandler的构造方法。

总结一下:

Proxy.newProxyInstance->生成代理类(代理类实现接口类)->代理类调用接口a实现->实际是通过InvocationHandler的invoke回调抛出->被代理对象调用前操纵->被代理对象调用接口a实现->被代理对象调用后操纵->完成代理行为;

Java标准代理Proxy只能实现interface级别的代理,因为这在ProxyClassFactory类中生成代理类字节码的时候有校验判断,当然CGLIB这个库就可以完成类的代理,参考 CGLib Wiki

SVG Path命令属性 Retrofit、Volley、HttpClient比较

comments powered by Disqus