引言
到目前为止,动态代理整个体系中应该只有代理类如何生成这部分对我们来说是完全空白的,这篇文章会帮我们迈出填补这个空白的第一步。我们会重点介绍Proxy中的ProxyClassFactory这个内部类,这个类做了字节码生成步骤之前的重要工作。
Proxy.newProxyInstance方法
从之前的分析我们知道,生成代理类$Proxy0是在Proxy.newProxyInstance方法中实现的。这个方法都做了哪些操作,我们来看一下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
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});
}
}
为了缩短版面,我去掉了权限检查和catch块这些无关紧要的代码。
方法的参数,我们已经很清楚了,就是ClassLoader(如果你对ClassLoader不是很了解,可以参考这个系列)、interfaces(代理类需要实现的接口,也就是IHello)和InvocationHandler,因为生成的动态代理类需要实现被增强类的接口,所以,一个interface的class数组应该就能唯一确定一个动态代理类了。
我们需要重点关注的是getProxyClass0这个方法,通过这个方法,得到了代表$Proxy0类的Class实例。
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);
}
方法首先检查interface的长度是否超过65535,如果超过,直接抛出异常。为什么会有这个限制?如果你对class文件的结构清楚的话,就能很轻松的回答这个问题,在class文件中,位于常量池之后的是访问标志,用来识别一些类或者接口层次的访问信息,而访问标志之后就是类索引、父类索引和接口索引集合,因为java中可以实现多个接口,所以接口索引集合中接口索引的数量可能会不止一个,这个数量是由一个u2类型(两个字节)的接口计数器来表明的。而u2类型的最大值就是65535。
回到正题,在进行了接口数量校验之后,调用的是proxyClassCache的get方法,proxyClassCache是一个WeakCache类实例,看来重点就是这个类了。我们从注释中能大概明白proxyClassCache.get方法的作用:如果实现了这些接口的、由这个类加载器加载的代理class已经存在的话,直接返回缓存的复制,否则使用ProxyClassFactory来创建代理class。
这篇文章的重点不是讲解WeakCache这个类的实现,但是略过WeakCache而直接讲解跟它相关的ProxyClassFactory肯定会有些突兀,因为我们不知道ProxyClassFactory这个类存在的目的是什么,所以,为了平滑的过渡,我们先将视线转移到WeakCache,看一下跟ProxyClassFactory相关的字段和构造方法。
WeakCache简介和与ProxyClassFactory的关系
这个类是在java.lang.reflect包里面的。
还是先翻译一下源码中的注释:
WeakCache是一个基于((key,sub-key)->value)的缓存映射。Keys和values是虚引用,sub-keys是强引用。keys直接被传到get方法,这个方法还需要一个parameter参数。Sub-keys是使用传入构造方法的SubKeyFactory参数根据keys和parameter计算出来的。Values是使用传入构造方法的ValueFactory根据keys和parameter计算出来的。Keys可以是null,subKeyFactory和valueFactory返回的sub-keys和values不能是null。sub-keys通过他们的equals方法来进行比较。当每次执行get、containsValue和size方法时,如果到keys的虚引用被清楚掉,那么缓存中的条目会被擦除。被清除的到individual values的虚引用不会导致擦除,但是这样的条目逻辑上会被当成不存在的并且在去请求这些values的key和subKey时会触发valueFactory的re-evaluation。
通过这个描述,我们大概知道这个类的作用了。
涉及到的字段
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;
我这里去掉了暂时不需要关注的字段。这两个成员变量是我们在Proxy中需要用到的,都是BiFunction类型,根据注释中的解释,一个用来计算subKey,一个用来是计算values,而这两个也是构造方法中需要给定的变量。
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
Proxy中的BiFunction
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
传入的两个参数是KeyFactory和ProxyClassFactory,这两个类肯定都是BiFunction的子类,它们都是Proxy的内部类,而ProxyClassFactory就是我们要分析的重要部分。
KeyFactory
/**
* A function that maps an array of interfaces to an optimal key where
* Class objects representing interfaces are weakly referenced.
*/
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);
}
}
}
通过注释和代码我们可以知道,这个Function映射一个interface的class的数组到一个最优的key,这个key里面的代表接口的class对象(们)是虚引用。
Key1、Key2和KeyX同样是Proxy的内部类,这里我还是把代码都贴出来吧:
Key1、Key2和KeyX
/*
* Key1 and Key2 are optimized for the common use of dynamic proxies
* that implement 1 or 2 interfaces.
*/
/*
* a key used for proxy class with 1 implemented interface
*/
private static final class Key1 extends WeakReference<Class<?>> {
private final int hash;
Key1(Class<?> intf) {
super(intf);
this.hash = intf.hashCode();
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
Class<?> intf;
return this == obj ||
obj != null &&
obj.getClass() == Key1.class &&
(intf = get()) != null &&
intf == ((Key1) obj).get();
}
}
/*
* a key used for proxy class with 2 implemented interfaces
*/
private static final class Key2 extends WeakReference<Class<?>> {
private final int hash;
private final WeakReference<Class<?>> ref2;
Key2(Class<?> intf1, Class<?> intf2) {
super(intf1);
hash = 31 * intf1.hashCode() + intf2.hashCode();
ref2 = new WeakReference<Class<?>>(intf2);
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
Class<?> intf1, intf2;
return this == obj ||
obj != null &&
obj.getClass() == Key2.class &&
(intf1 = get()) != null &&
intf1 == ((Key2) obj).get() &&
(intf2 = ref2.get()) != null &&
intf2 == ((Key2) obj).ref2.get();
}
}
/*
* a key used for proxy class with any number of implemented interfaces
* (used here for 3 or more only)
*/
private static final class KeyX {
private final int hash;
private final WeakReference<Class<?>>[] refs;
@SuppressWarnings("unchecked")
KeyX(Class<?>[] interfaces) {
hash = Arrays.hashCode(interfaces);
refs = (WeakReference<Class<?>>[])new WeakReference<?>[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
refs[i] = new WeakReference<>(interfaces[i]);
}
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
return this == obj ||
obj != null &&
obj.getClass() == KeyX.class &&
equals(refs, ((KeyX) obj).refs);
}
private static boolean equals(WeakReference<Class<?>>[] refs1,
WeakReference<Class<?>>[] refs2) {
if (refs1.length != refs2.length) {
return false;
}
for (int i = 0; i < refs1.length; i++) {
Class<?> intf = refs1[i].get();
if (intf == null || intf != refs2[i].get()) {
return false;
}
}
return true;
}
}
可以看到,当动态代理类需要实现1个、两个和两个以上接口的时候,分别会使用Key1、Key2和KeyX。Key1和Key2直接继承了WeakReference,而KeyX虽然没有继承WeakReference,却维护了一个WeakReference的数组,而这些虚引用都是到代表接口的Class对象。
所以针对KeyProxy,我们就可以理解为,当给定一个class数组(这个class数组里的每个class都代表一个接口,一个代理类需要实现的接口)时,返回到这些class的虚引用。
ProxyClassFactory
Proxy的代码比较长,我们分段解析:
/**
* 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();
最开始的注释表明了这个类的作用:一个根据classLoader和interface数组来生成、定义和返回代理类的工厂方法。也就是说,代表代理类$Proxy0的class对象,是在这里生成的。
两个成员变量,proxyClassNamePrefix和nextUniqueNumber,前者是所有代理类的名字的前缀,联想我们的$Proxy0,应该是的,后者是用来产生不同的代理类名称的数字。我们的$Proxy0这个数组就是0。
看apply方法的实现:
@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());
}
}
apply方法就是要完成根据classLoader和代表接口的class的数组返回代理类的class实例的任务。这部分代码对所有的interface进行了校验,包括被类加载器加载后生成的class对象是否与这个class是同一个、是否是接口的校验、是否重复的校验等等, 比较简单。
//生成代理类的包名
String proxyPkg = null; // package to define proxy class in
//生成的代理类的访问标志,默认是public final的。
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) {
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();
//如果接口的访问标志不是public的,那么生成的代理类的包名和接口报名相同
if (!Modifier.isPublic(flags)) {
//生成的代理类的访问标志设置为final
accessFlags = Modifier.FINAL;
//获取接口名称并得到包名称
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
//之前没有接口非public的,直接设置代理类的包名为当前接口的包名
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
//如果代理类实现不同包的接口并且接口都不是public的,报错
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
}
这部分代码上面我添加了注释,大致意思就是通过遍历每个接口的访问权限来得到生成的代理类存放在哪个包下。如果接口中有非public访问权限的,就存放在这个包下,但是不允许出现两个非public接口并且在不同的包下面的情况。
if (proxyPkg == null) {
//如果所有的包的访问权限都是public,就将生成的代理类放到com.sun.proxy下。
// 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.
*/
//调用sun.misc.ProxyGenerator来生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//根据二进制class文件获取相应的class实例。
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());
}
ProxyClassFactory类应该是动态代理中一个很重要的类了,虽然字节码生成不是在这个方法中实现的,但是它让我们知道了生成代理类的前置校验以及代理类的访问标志、存放位置的构造逻辑。
小结
经过艰难的源码分析,我们走出了探究动态代理底层机制的关键一步,先抛开WeakCache的简单介绍,我们知道了在Proxy类中,ProxyClassFactory这个BiFunction充当了生成代理类class实例的关键角色,它做了接口校验、包名生成和代理类访问标志这些工作。下一篇,我们就讲解字节码文件的真正生成流程。