Reflection

核心便是Class类,类图如下:

Class

方法获取

我们以getDeclaredMethods为例:

1@CallerSensitive
2public Method[] getDeclaredMethods() throws SecurityException {
3    return copyMethods(privateGetDeclaredMethods(false));
4}

privateGetDeclaredMethods:

 1private Method[] privateGetDeclaredMethods(boolean publicOnly) {
 2    Method[] res;
 3    ReflectionData<T> rd = reflectionData();
 4    if (rd != null) {
 5        res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
 6        if (res != null) return res;
 7    }
 8    // No cached value available; request value from VM
 9    res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
10    if (rd != null) {
11        if (publicOnly) {
12            rd.declaredPublicMethods = res;
13        } else {
14            rd.declaredMethods = res;
15        }
16    }
17    return res;
18}

publicOnly为false便说明需要获取各种访问级别的方法。

缓存

为加快方法获取的性能,这里会对反射的结果进行缓存。缓存以Class内部类ReflectionData对象的形式进行保存:

 1private static class ReflectionData<T> {
 2    volatile Field[] declaredFields;
 3    volatile Field[] publicFields;
 4    volatile Method[] declaredMethods;
 5    volatile Method[] publicMethods;
 6    volatile Constructor<T>[] declaredConstructors;
 7    volatile Constructor<T>[] publicConstructors;
 8    // Intermediate results for getFields and getMethods
 9    volatile Field[] declaredPublicFields;
10    volatile Method[] declaredPublicMethods;
11    volatile Class<?>[] interfaces;
12    // Value of classRedefinedCount when we created this ReflectionData instance
13    final int redefinedCount;
14    ReflectionData(int redefinedCount) {
15        this.redefinedCount = redefinedCount;
16    }
17}

reflectionData方法:

 1private ReflectionData<T> reflectionData() {
 2    SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
 3    int classRedefinedCount = this.classRedefinedCount;
 4    ReflectionData<T> rd;
 5    if (useCaches && reflectionData != null && (rd = reflectionData.get()) != null &&
 6        rd.redefinedCount == classRedefinedCount) {
 7        return rd;
 8    }
 9    // else no SoftReference or cleared SoftReference or stale ReflectionData
10    // -> create and replace new instance
11    return newReflectionData(reflectionData, classRedefinedCount);
12}

与之相关的两个属性定义:

1private volatile transient SoftReference<ReflectionData<T>> reflectionData;
2// Incremented by the VM on each call to JVM TI RedefineClasses()
3// that redefines this class or a superclass.
4private volatile transient int classRedefinedCount = 0;

从这里可以看出,每次JVM对当前类或其父类重新加载时都会导致classRedefinedCount的增加。Class使用了软引用进行缓存,只要虚拟机进行Full GC,便会对软引用指向的对象进行回收,所以软引用的对象的生存周期是当前至下一次Full GC。

变量useCaches决定了是否对反射结果进行缓存,其取值由方法checkInitted决定,相关源码:

1String val = System.getProperty("sun.reflect.noCaches");
2if (val != null && val.equals("true")) {
3    useCaches = false;
4}

有意思的细节: 这里使用字符串比较判断是否为true,而不是使用Boolean.getBoolean方法,这样是为了避免Boolean类的初始化,因为JVM规范定义了对类的静态方法的调用将导致类的初始化。

注意reflectionData方法判断缓存是否有效的条件里的这一个:

1rd.redefinedCount == classRedefinedCount

这就是说,只有缓存保存的类加载次数与类保存的相一致时缓存才是有效的。

过滤

当没有缓存或缓存已失效或被回收时,便需要向JVM请求获得相关信息,这里是通过native方法getDeclaredMethods0实现,类Reflection位于sun.reflect包下,JDK这样解释其功能: 将敏感的或是JVM内部的对象(属性或方法)过滤出去。

拷贝

copyMethods实现:

1private static Method[] copyMethods(Method[] arg) {
2    Method[] out = new Method[arg.length];
3    ReflectionFactory fact = getReflectionFactory();
4    for (int i = 0; i < arg.length; i++) {
5        out[i] = fact.copyMethod(arg[i]);
6    }
7    return out;
8}

跳过复杂的调用关系,真正进行拷贝的其实就是Method的copy方法:

 1Method copy() {
 2    if (this.root != null)
 3        throw new IllegalArgumentException("Can not copy a non-root Method");
 4    Method res = new Method(clazz, name, parameterTypes, returnType,
 5                            exceptionTypes, modifiers, slot, signature,
 6                            annotations, parameterAnnotations, annotationDefault);
 7    res.root = this;
 8    // Might as well eagerly propagate this if already present
 9    res.methodAccessor = methodAccessor;
10    return res;
11}

可以看出,每次返回的都是一个全新的Method对象,新对象的root属性指向原对象,一个Method对象及其副本共享一个methodAccessor,methodAccessor对象可以看做是对JVM相应方法的引用。

那这里为什么要进行拷贝呢?