Android 设计模式

JDK动态代理实现源码浅析

2022-06-18  本文已影响0人  茂茂的小破号

本篇的假设读者了解代理模式,以及会使用Java JDK动态代理。如果不熟悉,可以先查看另一篇文章:动态代理-Java - 简书 (jianshu.com)

本文将从三个方面来解析JDK动态代理实现,分别如下:

首先简要复习一下Java JDK动态代理的使用方法,示例:使用JDK动态代理实现日志打印。

JDK动态代理的使用

首先创建一个UserDao接口,UserDao定义了setUserInfo与getUserInfo两个方法。

public interface UserDao {
    public void setUserInfo(String userInfo);
    public String getUserInfo();
}

接着创建UserDao的实现类UserDaoImpl:

public class UserDaoImpl implements UserDao{
    private String userInfo;
    @Override
    public void setUserInfo(String userInfo) {
        this.userInfo = userInfo;
        System.out.println("设置信息:" + userInfo);
    }
    @Override
    public String getUserInfo() {
        System.out.println("获取信息:" + userInfo);
        return userInfo;
    }
}

然后,创建代理逻辑实现类LogJdkHandler,该类实现了java.lang.reflect.InvocationHandler的接口,并且持有被代理的对象的引用:

public class LogJdkHandler implements InvocationHandler{
    private Object target;

    public LogJdkHandler(Object userDao) {
        this.target = userDao;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // proxy是动态生成的代理类的对象
        // method是被触发的方法
        // args是方法入参列表
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }
    private void before(){
        System.out.println("log before" + (new Date().toString()));
    }
    private void after(){
        System.out.println("log after" + (new Date().toString()));
    }
}

最后是为对象创建代理对象:

// 1. 创建代理对象
UserDao userDao = new UserDaoImpl();
// 2. 创建代理的处理类
LogJdkHandler handler = new LogJdkHandler(userDao);
// 3. 使用Proxy.newProxyInstance生成代理对象
UserDao proxy = (UserDao)Proxy.newProxyInstance(
    userDao.getClass().getClassLoader(), 
    userDao.getClass().getInterfaces(), 
    handler);
proxy.setUserInfo("我是jdk proxy");
System.out.println(proxy.getUserInfo());

创建代理对象调用的是Proxy.newProxyInstance静态方法,该静态方法定义如下:

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

带查看源码前先看一下JDK动态生成的代理类的结构(需在在创建代理对象前运行下列代码):

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

生成的代理对象字节码会被保存在com\sun\proxy目录下,反编译字节码可以查看到源码:

package com.sun.proxy;

import com.maomao.patterndesign.p1_proxy.UserDao;
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 UserDao {
  private static Method m1;
  
  private static Method m2;
  
  private static Method m3;
  
  private static Method m4;
  
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler) {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject) {
    try {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final String toString() {
    try {
      return (String)this.h.invoke(this, m2, null);
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final String getUserInfo() {
    try {
      return (String)this.h.invoke(this, m3, null);
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final void setUserInfo(String paramString) {
    try {
      this.h.invoke(this, m4, new Object[] { paramString });
      return;
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final int hashCode() {
    try {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("com.maomao.patterndesign.p1_proxy.UserDao").getMethod("getUserInfo", new Class[0]);
      m4 = Class.forName("com.maomao.patterndesign.p1_proxy.UserDao").getMethod("setUserInfo", new Class[] { Class.forName("java.lang.String") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    } catch (NoSuchMethodException noSuchMethodException) {
      throw new NoSuchMethodError(noSuchMethodException.getMessage());
    } catch (ClassNotFoundException classNotFoundException) {
      throw new NoClassDefFoundError(classNotFoundException.getMessage());
    } 
  }
}

可以看到新创建的代理类$Proxy0继承了Proxy类并实现了被代理对象的接口方法。新的代理类有一个构造函数,该构造函数入参是InvocationHandler即我们传入的LogJdkHandler对象。

public $Proxy0(InvocationHandler paramInvocationHandler) {
    super(paramInvocationHandler);
}

构造函数里面通过super(paramInvocationHandler)调用了父类Proxy的构造函数,将入参InvocationHandler保存在成员变量里面:

protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}

还可以看到$Proxy0实现了getUserInfo和setUserInfo,在实现类中调用了如下方法,即调用了我们自己在LogJdkHandler类实现的invoke方法。

(String)this.h.invoke(this, m2, null);

第一个入参是代理对象本身,第二个入参是被触发的方法对象,第三个入参是被触发方法的对象。

到目前为止,JDK动态代理生成的代理类结构已经很明晰了,那么代理类是被如何动态创建并且加载入内存的呢?下一小节将进行一步一步的分析。

JDK动态代理类的生成方式

(1)Proxy.newProxyInstance

首先我们是通过调用Proxy.newProxyInstance来创建代理对象,该方法中有关键的三行逻辑:

Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});

其中我们要深入了解的是cl.getProxyClass0方法,查看代理类是如何被生成并加载进内存的。

(2)cl.getProxyClass0

点击进去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);
    }

该方法的入参是我们调用Proxy.newProxyInstance传进来的被代理对象的类加载器ClassLoader loader与接口列表Class<?>... interfaces。return那行代码上面的那串英文的意思是如果实现接口列表的加载器代理类存在的话,方法proxyClassCache.get会直接返回代理类,否则该方法会通过ProxyClassFactory工厂类去创建代理类,即调用proxyClassCache.get方法获取,那么proxyClassCache是什么呢?该参数定义在了Proxy类中,如下

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

proxyClassCache是WeakCache类的对象,我们需要了解的是创建该对象用到了两个类的对象以及WeakCache类,分别如下:

① KeyFactory
顾名思义,生成key的工厂。生成的key是用来干什么的,key又是怎样生成的?这里的解答是,key是用来作为代理类的唯一标志,用于获取类的。至于如何生成的可以查看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);
          }
      }
  }

点击进入KeyFactory可以看一下它是Proxy类的内部类,很一目了然,该类中主要有一个apply方法,入参是熟悉的类加载器与接口列表。apply方法会根据类加载器以及接口数量来生成唯一key,具体如何生成这里不细致展开了。

② ProxyClassFactory
该类也是Proxy类的内部类,根据名称可以得知,该类是创建代理类的工厂。

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

首先是代理类的两个字段,表示代理类的名称是:类名前缀+编号。

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

该工厂类的一个主要方法也是apply方法,入参也是类加载器与接口列表,用于调用生成代理类

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

其中重要的一行代码如下

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

调用ProxyGenerator.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;
    }

该方法的入参var1var2var3分别是类名、接口列表,以及接口实现方法修饰符标志(PUBLIC或者FINAL)。该类首先调用new ProxyGenerator(var0, var1, var2);来生成代理生成器var3然后调用var3.generateClassFile()来生成类的byte[]数组。在返回代理类byte[]数组之前会判断是否将代理类保存到磁盘上,即前文我们用于设置保存代理类文件的一串代码:

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

下面来简要看一下generateClassFile方法

  private byte[] generateClassFile() {
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }
       .....

该方法执行具体的生成代理类的字节码,可以看到,该方法首先会将Object的hashCode、equals、toString方法加入要代理的方法,然后遍历接口列表将接口定义的方法加入到要代理的方法中。具体的实现这里不细致展开了,感兴趣的可以点击查看完整的ProxyGenerator类的实现逻辑。

③ WeakCache类
到这里想必有些晕了,让我们回到Proxy类的存放代理类的缓存字段proxyClassCache

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

WeakCache是一个弱缓存类,其类描述如下

Cache mapping pairs of (key, sub-key) -> value. Keys and values are weakly but sub-keys are strongly referenced. Keys are passed directly to get method which also takes a parameter. Sub-keys are calculated from keys and parameters using the subKeyFactory function passed to the constructor. Values are calculated from keys and parameters using the valueFactory function passed to the constructor. Keys can be null and are compared by identity while sub-keys returned by subKeyFactory or values returned by valueFactory can not be null. Sub-keys are compared using their equals method. Entries are expunged from cache lazily on each invocation to get, containsValue or size methods when the WeakReferences to keys are cleared. Cleared WeakReferences to individual values don't cause expunging, but such entries are logically treated as non-existent and trigger re-evaluation of valueFactory on request for their key/subKey.

大致意思是WeakCache可用作Map,可以使用get(key, subKey)的方法来获取值。在代理类里key是使用我们传入的类加载器ClassLoader loader,subKey是接口列表Class<?>... interfaces作为subKey,值则是我们想要获取的动态生成的代理类。从描述中还可以读到key与value是弱引用,subKey是强引用,弱引用含义如下:

弱引用(Weak Reference):弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

WeakCache类描述中还说到subKey是调用subKeyFactory计算得到的,即是上文中提到的Proxy类的内部类KeyFactory,该类有一个apply方法,接受classLoader与interfaces作为参数,以生成key。

proxyClassCache.get

让我们回到最初的proxyClassCache.get(loader, interfaces)代码处,点击进去查看源码如下:

    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

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

该方法入参是key与parameter,即我们传入的classLoader与interfaces。首先看的是下面两行代码:

Object cacheKey = CacheKey.valueOf(key, refQueue);
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);

WeakCache类会首先查看缓存中有没有计算过的subkey,如果没有的话会调用subKeyFactory.apply方法生成subKey,如下

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

获取到的subKey后,下一步根据subKey获取值,即我们想要生成的代理类。

但是,根据subKey还不能直接拿到想要的值,需要根据subKey获取到生成值的工厂类,然后调用工厂类对象的apply方法才能获取,如下:

Supplier<V> supplier = valuesMap.get(subKey);

其中supplier获取的就是valueFactory也就是我们在Proxy类看到的ProxyClassFactory类的对象,还记得这个类的apply方法吗?该方法接收classLoader与interfaces生成代理类。忘记的话可以往前翻翻。

这里获取supplier是使用的懒加载的方法,即在第一次用到时才去创建工厂类,即while(ture)里面的逻辑。

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
}

那么获取代理类的方法是在哪里被调用的呢,while(true)里面有一行代码

if (supplier != null) {
    // supplier might be a Factory or a CacheValue<V> instance
    V value = supplier.get();
    if (value != null) {
        return value;
    }
}

是调用了supplier.get()方法获取的代理类,而supplier就是Factory对象,Factory长什么样呢?点开该类,看到该类是WeakCache的内部类,其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 = Objects.requireNonNull(valueFactory.apply(key, parameter));

该段代码调用了valueFactory.apply方法,valueFactory是什么呢?下面是valueFactory在WeakCache类的定义与初始化:

private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
    this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
    this.valueFactory = Objects.requireNonNull(valueFactory);
}

可以看到valueFactory正是我们在Proxy类中定义proxyClassCache时传入的代理工厂类ProxyClassFactory的对象。

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

调用valueFactory.apply方法,就是在调用ProxyClassFactory中定义的apply方法,这样,整个调用的逻辑就都走通了。

上一篇 下一篇

猜你喜欢

热点阅读