Java反射机制(二)—— 深入JDK了解反射(JDK1.8)

…衆ロ難τιáo~ 提交于 2019-12-17 21:15:48

在这里插入图片描述
《Java反射机制(一)—— 使用反射》一文中我们提到,在类的初始化阶段会创建对应类的一个Class对象,Class对象里还缓存了该类的所有Constructor对象、Method对象、Filed对象。这个初始化阶段是在JDK底层以C语言实现的,有兴趣可以自行查看。本文只对反射机制是如何使用Class对象实现实例创建、方法调用、属性访问进行说明。

1.反射创建对象的过程

1.1反射获取Constructor对象的过程

反射创建一个类的对象是从获取该类的Constructor(构造器)对象开始的。Class对象获取特定构造函数对象的方法是getDeclaredConstructor(Class<?>… parameterTypes) 。

JDK源码
/*Class*/
	@CallerSensitive
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.DECLARED);
    }
	
    private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                        int which) throws NoSuchMethodException
    {
        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                                constructor.getParameterTypes())) {
                return getReflectionFactory().copyConstructor(constructor);
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
    }

从上面两个方法不难看出,要获取目标Constructor对象分为两步,获取Class对象的所有构造器对象根据参数类型数组获取到特定的构造器对象

获取所有构造器对象的方法是privateGetDeclaredConstructors,在该方法中,会先试图获取Class对象在类初始化阶段就缓存的该类的所有Constructor对象,那这个缓存在什么位置呢,这个缓存就是Class的属性private volatile transient SoftReference<ReflectionData> reflectionData。这个属性的类型是SoftReference(软引用),所谓软引用就是在资源紧张的情况下GC会进行回收,这就可能导致缓存丢失。SoftReference的泛型是ReflectionData,ReflectionData就是缓存数据的真正格式。我们来看一下ReflectionData是怎么缓存数据的。

JDK源码
/*Class$ReflectionData*/
	private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        // Intermediate results for getFields and getMethods
        volatile Field[] declaredPublicFields;
        volatile Method[] declaredPublicMethods;
        volatile Class<?>[] interfaces;

        // Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;

        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
    }

ReflectionData用数据的形式,将所有的Constructor、Method、Field对象存储下来供反射进行使用。

在新版本的JDK中对该缓存进行了优化,放弃使用ReflectionData存储,而是在Class中直接将Constructor、Method、Field数组放进软引用中作为缓存。这样就将缓存分散,当资源紧张时,不会一次全部被GC回收

了解了Class对象如何缓存Constructor,下面我们就来看privateGetDeclaredConstructors方法的源码。

JDK源码
	private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
        checkInitted();
        Constructor<T>[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        if (isInterface()) {
            @SuppressWarnings("unchecked")
            Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
            res = temporaryRes;
        } else {
            res = getDeclaredConstructors0(publicOnly);
        }
        if (rd != null) {
            if (publicOnly) {
                rd.publicConstructors = res;
            } else {
                rd.declaredConstructors = res;
            }
        }
        return res;
    }

上面我们说了软引用的缓存在资源紧张的情况下会被GC回收导致。所以在获取Constructor方法时也是分为两种情况,一种是缓存存在,一种是缓存为空。

  • 当缓存存在的时候,JDK会直接从缓存中取出Constructor对象数组,需要注意的就是会根据publicOnly决定是取所有Constructor对象的数组还是仅取public方法的Constructor数据。由于在getConstructor0在调用privateGetDeclaredConstructors时已经传入了false值,所以此处是取所有Constructor对象的数组。
  • 当缓存资源已被GC回收的时候,程序将向JVM请求重建该缓存。重建之后再将Constructor数据返回。

至此,已经获取到了Class对象的所有Constructor对象,下一步就需要根据参数类型数组来获取目标的Constructor对象。我们回到getDeclaredConstructor方法。

JDK源码
/*Class.getConstructor0*/
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                                constructor.getParameterTypes())) {
                return getReflectionFactory().copyConstructor(constructor);
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));

代码会遍历所有的Constructor对象,将其parameterTypes进行比对,当参数类型比对成功就返回该对象,若遍历结束也未找到目标构造器,则抛出NoSuchMethodException异常。

值得注意的是,这里不是直接返回缓存中的Constructor对象,而是利用ReflectionFactory复制了一个Constructor对象再返回。

调用ReflectionFactory对象的copyConstructor方法,在该方法中又使用LangReflectAccess接口的实现类ReflectAccess对象的copyConstructor方法最终对Constructor对象进行复制。

这句话有点绕,是因为JDK对该过程进行了层层封装。有意思的是,我们打开最终复制Constructor的方法,发现就是调用了Constructor对象自己的copy方法进行复制,然后返回复制的得到的对象。

JDK源码
/*ReflectAccess.copyConstructor*/
	public <T> Constructor<T> copyConstructor(Constructor<T> arg) {
        return arg.copy();
    }

1.2利用Constructor对象创建实例

反射要创建一个实例,需要调用Constructor对象的newInstance方法。

JDK源码
/**Constructor.newInstance**/
@CallerSensitive
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

在方法的第一段代码是对创建实例的权限进行验证,若在调用newInstance方法前已经设置了setAccess(true)则验证通过,否则将根据访问级别进行验证,若验证通过则程序继续往下执行,否则会抛出IllegalAccessException异常。

方法的第二段是在验证正在创建实例中的class是否为ENUM,因为枚举类是无法创建实例的,所以若为ENUM则抛出IllegalArgumentException异常。

在两次验证都通过后,方法的第三段代码将获取ConstructorAccessor进行实例创建。创建的实力的过程在NativeConstructorAccessorImpl的native方法newInstance0中。

2.反射调用方法

2.1获取目标方法对象

反射调用一个方法从获取Method对象开始。我们先看一下获取Method对象的getDeclaredMethod(String name, Class<?>… parameterTypes)方法源码。

JDK源码
/*Class*/
@CallerSensitive
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

从这段代码我们可以看出,获取Method对象的语句是searchMethods(privateGetDeclaredMethods(false) , name , parameterTypes)。首先是调用privateGetDeclaredMethods(false)方法获取该Class对象的所有Method对象数组,然后调用searchMethods方法利用方法名和参数类型搜索Method对象数组获取目标Method对象,若最后没有获取到目标方法将抛出NoSuchMethodException异常。为了进一步了解这个过程,我们先来看一下privateGetDeclaredMethods方法的源码。

JDK源码
/*Class*/
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
        checkInitted();
        Method[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
        if (rd != null) {
            if (publicOnly) {
                rd.declaredPublicMethods = res;
            } else {
                rd.declaredMethods = res;
            }
        }
        return res;
    }

阅读这段代码会发现和获取所有构造器对象的方法privateGetDeclaredConstructors非常相似。因为他们实现原理是一样的,privateGetDeclaredMethods也是通过获取软引用缓存获取所有的Method对象。当缓存内容已被GC回收时,将请求JVM重建缓存。

获取到的所有Method对象组成的数组作为参数传入searchMethods中搜索获取目标的Method。

private static Method searchMethods(Method[] methods,
                                        String name,
                                        Class<?>[] parameterTypes)
    {
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }

在这个方法中,会根据传入的方法名称和参数列表与所有的Method进行匹配,若匹配到了则复制该Method返回,若未搜索到则返回null。

2.2 反射调用方法

在前文中介绍了获取Method对象的过程,下一步就是调用Method对象的invoke(Object obj, Object… args)方法调用该方法。我们知道在面向对象里,调用一个方法,离不开方法的调用者和参数列表。invoke方法的第一个参数就是目标方法的调用者对象,第二个参数就目标方法的参数列表参数类型组成的数组。

JDK源码
/*Method.invoke*/
@CallerSensitive
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }

与构造器对象创建实例相似,invoke调用方法也需要先对权限进行验证,若是Method对象设置了setAccess(true),则验证直接通过。否则需要验证方法调用者是否对目标方法有访问权限。

权限验证通过后会获取MethodAccessor对象,实际是调用MethodAccessor接口的底层实现类NativeMethodAccessor对象的invoke方法,其invoke方法又调用native方法invoke0最终实现对方法的调用。

3.反射访问Field

反射获取Field对象的方式与Constructor、Method一样,都是复制SoftReference缓存的Filed。获取Field值也是直接调用Field对象的get()方法。

需要重点说明就是对Filed值的修改。修改一个Field对象的值,需要通过调用对应FieldAccessor的set(Object obj, Object obj1)实现。而不同的Field有着不同的FieldAccessor。Field通过ReflectionFactory的newFieldAccessor方法创建一个FieldAccessor。而ReflecttionFactory调用**UnsafeFieldAccessorFactory.newFieldAccessor(field, flag)**最终实现了FieldAccessor的创建。

用一张图来表示UnsafeFieldAccessorFactory如何选择创建不同类型Field对应的Accessor。
在这里插入图片描述

  • static属性: 普通静态Field创建的是UnsafeStaticXXXFieldAccessorImpl对象,该对象会在set操作时先获取Field的调用者对应的类的Class对象(该操作的主要针对调用Field对象的时候传入实例对象,而不是Class对象的情况),再将该Class对象传入native方法更改属性值。
  • final static属性: 静态常量Field创建的是UnsafeQualifiedStaticXXXFieldAccessorImpl对象,该对象在创建时会给isReadOnly传值为true,所以当静态常量利用反射进行写操作时,将会抛出异常。
  • 普通实例属性: 普通实例Field(即没有static也没有final修饰的属性)创建的是UnsafeXXXFieldAccessorImpl对象,该对象会直接调用底层native方法更改属性值。
  • fianl属性: 常量Field创建的是UnsafeQualifiedXXXFieldAccessorImpl对象,该对象在创建时会给isReadOnly传值为false,所以常量利用反射进行写操作时是可以正常进行的,但最后会发现,调用set方法后,Field对应的值会被更改,但实际上实例的常量未被更改。

我们来看一下UnsafeFieldAccessorFactory的代码。

JDK源码
/*sun.reflect.UnsafeFieldAccessorFactory*/
class UnsafeFieldAccessorFactory
{

    UnsafeFieldAccessorFactory()
    {
    }

    static FieldAccessor newFieldAccessor(Field field, boolean flag)
    {
        Class class1 = field.getType();
        boolean flag1 = Modifier.isStatic(field.getModifiers());
        boolean flag2 = Modifier.isFinal(field.getModifiers());
        boolean flag3 = Modifier.isVolatile(field.getModifiers());
        boolean flag4 = flag2 || flag3;
        boolean flag5 = flag2 && (flag1 || !flag);
        if(flag1)
        {
            UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(field.getDeclaringClass());
            if(!flag4)
            {
                if(class1 == Boolean.TYPE)
                    return new UnsafeStaticBooleanFieldAccessorImpl(field);
                if(class1 == Byte.TYPE)
                    return new UnsafeStaticByteFieldAccessorImpl(field);
                if(class1 == Short.TYPE)
                    return new UnsafeStaticShortFieldAccessorImpl(field);
                if(class1 == Character.TYPE)
                    return new UnsafeStaticCharacterFieldAccessorImpl(field);
                if(class1 == Integer.TYPE)
                    return new UnsafeStaticIntegerFieldAccessorImpl(field);
                if(class1 == Long.TYPE)
                    return new UnsafeStaticLongFieldAccessorImpl(field);
                if(class1 == Float.TYPE)
                    return new UnsafeStaticFloatFieldAccessorImpl(field);
                if(class1 == Double.TYPE)
                    return new UnsafeStaticDoubleFieldAccessorImpl(field);
                else
                    return new UnsafeStaticObjectFieldAccessorImpl(field);
            }
            if(class1 == Boolean.TYPE)
                return new UnsafeQualifiedStaticBooleanFieldAccessorImpl(field, flag5);
            if(class1 == Byte.TYPE)
                return new UnsafeQualifiedStaticByteFieldAccessorImpl(field, flag5);
            if(class1 == Short.TYPE)
                return new UnsafeQualifiedStaticShortFieldAccessorImpl(field, flag5);
            if(class1 == Character.TYPE)
                return new UnsafeQualifiedStaticCharacterFieldAccessorImpl(field, flag5);
            if(class1 == Integer.TYPE)
                return new UnsafeQualifiedStaticIntegerFieldAccessorImpl(field, flag5);
            if(class1 == Long.TYPE)
                return new UnsafeQualifiedStaticLongFieldAccessorImpl(field, flag5);
            if(class1 == Float.TYPE)
                return new UnsafeQualifiedStaticFloatFieldAccessorImpl(field, flag5);
            if(class1 == Double.TYPE)
                return new UnsafeQualifiedStaticDoubleFieldAccessorImpl(field, flag5);
            else
                return new UnsafeQualifiedStaticObjectFieldAccessorImpl(field, flag5);
        }
        if(!flag4)
        {
            if(class1 == Boolean.TYPE)
                return new UnsafeBooleanFieldAccessorImpl(field);
            if(class1 == Byte.TYPE)
                return new UnsafeByteFieldAccessorImpl(field);
            if(class1 == Short.TYPE)
                return new UnsafeShortFieldAccessorImpl(field);
            if(class1 == Character.TYPE)
                return new UnsafeCharacterFieldAccessorImpl(field);
            if(class1 == Integer.TYPE)
                return new UnsafeIntegerFieldAccessorImpl(field);
            if(class1 == Long.TYPE)
                return new UnsafeLongFieldAccessorImpl(field);
            if(class1 == Float.TYPE)
                return new UnsafeFloatFieldAccessorImpl(field);
            if(class1 == Double.TYPE)
                return new UnsafeDoubleFieldAccessorImpl(field);
            else
                return new UnsafeObjectFieldAccessorImpl(field);
        }
        if(class1 == Boolean.TYPE)
            return new UnsafeQualifiedBooleanFieldAccessorImpl(field, flag5);
        if(class1 == Byte.TYPE)
            return new UnsafeQualifiedByteFieldAccessorImpl(field, flag5);
        if(class1 == Short.TYPE)
            return new UnsafeQualifiedShortFieldAccessorImpl(field, flag5);
        if(class1 == Character.TYPE)
            return new UnsafeQualifiedCharacterFieldAccessorImpl(field, flag5);
        if(class1 == Integer.TYPE)
            return new UnsafeQualifiedIntegerFieldAccessorImpl(field, flag5);
        if(class1 == Long.TYPE)
            return new UnsafeQualifiedLongFieldAccessorImpl(field, flag5);
        if(class1 == Float.TYPE)
            return new UnsafeQualifiedFloatFieldAccessorImpl(field, flag5);
        if(class1 == Double.TYPE)
            return new UnsafeQualifiedDoubleFieldAccessorImpl(field, flag5);
        else
            return new UnsafeQualifiedObjectFieldAccessorImpl(field, flag5);
    }
}

看起来代码很长,但是我们把主要的逻辑提取出来发现其实并不复杂

static FieldAccessor newFieldAccessor(Field field, boolean flag)
    {
        Class class1 = field.getType();
        boolean flag1 = Modifier.isStatic(field.getModifiers());//判断是否为static
        boolean flag2 = Modifier.isFinal(field.getModifiers());//判断是否为fianl
        boolean flag3 = Modifier.isVolatile(field.getModifiers());//判断是否为volatile
        boolean flag4 = flag2 || flag3; //final或者volatile
        boolean flag5 = flag2 && (flag1 || !flag);//判断是否为fianl static
        if(flag1)
        {
            UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(field.getDeclaringClass());
            if(!flag4)        //static但非final
            {
                    ···
                return new UnsafeStaticObjectFieldAccessorImpl(field);
            }
                               //static final
                    ···
                return new UnsafeQualifiedStaticObjectFieldAccessorImpl(field, flag5);
        }
        if(!flag4)           //非static非final
        {
                ···
            return new UnsafeObjectFieldAccessorImpl(field);
        }
                ···           //final
            return new UnsafeQualifiedObjectFieldAccessorImpl(field, flag5);
    }

从以上简化后逻辑可以看出,静态常量和普通常量在创建Accessor时除了传入当前Field对象,还会传入flag5,这个flag5会对Accessor的isReadOnly进行赋值。如果仔细梳理你会发现,当创建静态常量Accessor时flag5永远是true。创建普通常量Accessor时,若Field未设置isAccess(true)则flag5为true,set方法将无法正常调用,若设置了isAccess(true)则flag5为false,set方法可以正常调用,但也只能进行“假写”操作。

~~~欢迎留言交流~~~

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!