您的位置 首页 java

Java:反射(newInstance)源码分析

Java:源码分析—反射(实例化对象:newInstance)

今天我们来通过源代码,看看 Java 中反射是如何工作的。

反射,其实在我们日常的编程过程中可能用的并不多,但是第三方的库,这个技术确实应用非常广泛的,包括我们在开发 Android 应用时,有时为了操作一些不开放的系统属性时,也会用到反射。

这里我们将通过分析反射的运行,来看看 Class 这个类的功能。开始之前,先提到两个静态的整型变量,在 java.lang.reflect.Member 中。这两个变量后面会经常提到。

     /**
     * 所有的public成员,包括继承关系和接口
     */
     PUBLIC  static final int PUBLIC = 0;
 
    /**
     * 所有访问级别的成员,不包括继承(父类、子类)及接口
     */
    public static final int DECLARED = 1;  

再看几个属性,在内部类 ReflectionData 中,保存着一些反射获取的方法、字段等信息集合。

 //存放自己的所有字段,不包括继承关系的
volatile Field[] declaredFields;
//存放包括继承关系的public的字段
volatile Field[] publicFields;
//存放自己的,不包括继承关系的方法
volatile Method[] declaredMethods;
//存放包括继承关系的public的方法
volatile Method[] publicMethods;
//自己的,不包括继承关系的 构造函数 
volatile  constructor <T>[] declaredConstructors;
//包括继承关系的public的构造函数
volatile Constructor<T>[] publicConstructors;
// public and declared
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
//接口
volatile Class<?>[] interfaces;  

包括后面提到的 getMethods getDeclaredMethods 之类的,都是这个规则的区别。

先定义两个用来测试的类, Temp 及它的子类 TempChild 。这两个类里面,分别定义了三种修饰的方法和属性( public protected private )。

 public class Temp {
    public int public_Field_1;
    private int private_Field_2;
    protected int protected_Field_3;
 
    public Temp(){
        System.out.println("Temp Created");
    }
    public  Void  public_Foo_1(){
        System.out.println("public_Foo_1");
    }
    private void private_Foo_2(){
        System.out.println("private_Foo_2");
    }
    protected void protected_Foo_3(){
        System.out.println("protected_Foo_3");
    }
}
 
public class TempChild extends Temp {
    public int public_Child_Field_1;
    private int private_Child_Field_2;
    protected int protected_Child_Field_3;
    public void public_Child_Foo_1(){
        System.out.println("public_Child_Foo_1");
    }
    private void private_Child_Foo_2(){
        System.out.println("private_Child_Foo_2");
    }
    protected void protected_Child_Foo_3(){
        System.out.println("protected_Child_Foo_3");
    }
}  

一个对象,最基本的就是构造、方法、属性,所以反射步骤,我们就按照这个来进行,逐步分析一下整个流程。

下面我们写代码,来反射 TempChild 这个类。

 /**
*反射需要完整的包名
*通过包名获取要反射的对象
*/
Class<?> obj = Class.forName("com.myweb.demo.TempChild");
/**
*实例化这个对象
*/
Object temp = obj.newInstance();  

反射,最终也要将对象实例化, newInstance 就是负责这个工作的,具体看看 newInstance 的实现。 需要注意的是newInstance是无参数的,如果你自己定义了一个带参数的构造函数,就会报错 。如果想使用带参数的构造,可以通过 Constructor 对象。

 public T newInstance()
        throws InstantiationException, IllegalAccessException
{
//先检查是否有设置权限,比如读写文件
    if (System.getSecurityManager() != null) {
        checkMemberAccess(Member.PUBLIC,  Reflection .getCallerClass(), false);
    }
 
    //检查构造函数是否存在
    if (cachedConstructor == null) {
    //如果还没找到构造函数,再判断要反射的类是不是Class类,如果是,抛出异常,不能反射Class类
        if (this == Class.class) {
            throw new IllegalAccessException(
                    "Can not call newInstance() on the Class for java.lang.Class"
            );
        }
        try {
            Class<?>[] empty = {};
            //获取所有访问级别的构造方法,并返回一个匹配的public的构造对象
            final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
            //对于构造函数,允许获取更大的权限
            java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                            //设置标记,不再检查权限
                            c.setAccessible(true);
                            return null;
                        }
                    });
            cachedConstructor = c;
        } catch (NoSuchMethodException e) {
            throw (InstantiationException)
                    new InstantiationException(getName()).initCause(e);
        }
    }
    Constructor<T> tmpConstructor = cachedConstructor;
    // Security check (same as in java.lang.reflect.Constructor)
    int modifiers = tmpConstructor.getModifiers();
    //判断是不是public的
    if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
        //如果不是,就要判断调用者是否是当前的进程使用者,也就是判断是否是第三方调用
        Class<?> caller = Reflection.getCallerClass();
        if (newInstanceCallerCache != caller) {
            Reflection.ensureMemberAccess(caller, this, null, modifiers);
            newInstanceCallerCache = caller;
        }
    }
    // Run constructor
    try {
        //创建对象
        return tmpConstructor.newInstance((Object[])null);
    } catch (InvocationTargetException e) {
        Unsafe.getUnsafe().throwException(e.getTargetException());
        // Not reached
        return null;
    }
}  

创建对象时,先判断是否设置了系统权限,这个可以通过文件配制好的,比如对本地的某些文件,是否允许读写,网络是否可以访问等。可参考 Java 安全模型介绍( ),官方文档( ),后面会看到多处 AccessController.doPrivileged 的调用,都可以参考这两个文档。

如果设置了权限,那就判断要访问的对象是否符合你的权限设置。来看 checkMemberAccess 方法。

 //判断是否允许访问成员,如果被拒绝,抛出SecurityException异常
private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {
    final SecurityManager s = System.getSecurityManager();
    if (s != null) {
        /* 默认的可以访问PUBLIC成员,
         * 也可以访问具有相同Loader和Caller的类
         * 其他的,需要设置RuntimePermission("accessDeclaredMembers")权限
         */
        final ClassLoader ccl = ClassLoader.getClassLoader(caller);
        final ClassLoader cl = getClassLoader0();
        if (which != Member.PUBLIC) {
            if (ccl != cl) {
                //既不是PUBLIC的,也不是来自同一个Loader和Caller的,那就通过SecurityManager去检查权限
                s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
            }
        }
        this.checkPackageAccess(ccl, checkProxyInterfaces);
    }
}  

系统默认是可以访问所有Public的成员的,还可以访问具有相同加载器和调用者的类,其他的都要向系统申请 RuntimePermission(“accessDeclaredMembers”) 权限。这两项如果都不符合,就会通过 SecurityManager checkPermission 去检查是否配制了相关的权限。

回到newInstance进行看,如果权限符合之后,就是获取构造函数。

获取构造函数,需要获取包括父类、子类的构造,所以使用的是 DECLARED

 //empty就是构造函数的参数,newInstance是不带参数的,所以是一个空数组
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, 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));
}  

privateGetDeclaredConstructors 获取到 PUBLIC 的构造方法的数组,然后遍历这个数组,判断传入的参数集合与反射到的构造集合中的参数集合是否一致,如果一致,我们才能使用。

 private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
    //检查系统的一些属性是否准备好,可以访问了
    checkInitted();
    Constructor<T>[] res;
    Class.ReflectionData<T> rd = reflectionData();
    //判断是否存在已经反射的结果
    if (rd != null) {
        //如果有,根据是否PUBLIC获取对应的构造函数的集合,如果存在,返回
        res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
        if (res != null) return res;
    }
    // 检查你反射的是不是接口
    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;
}  

checkInitted 用来检查系统是否准备好,属性是否可以立即访问,比如想通过 System.out 去打印日志,就要判断 System.out 是否可用,如果没准备好,就给予最高权限去修改。

 private static void checkInitted() {
    if (initted) return;
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            //如果System.out不可用
            if (System.out == null) {
                return null;
            }
 
            // Doesn't use Boolean.getBoolean to avoid class init.
            String val = System.getProperty("sun.reflect.noCaches");
            /**
            *noCaches是否存在,来决定是否用缓存中的反射数据
            *见private ReflectionData<T> reflectionData()
            */
            if (val != null && val.equals("true")) {
                useCaches = false;
            }
 
            initted = true;
            return null;
        }
    });
}  

checkInitted 通过之后,先获取 reflectionData ,其中的属性,在文字开头已经说明。然后判断 reflectionData 是否存在,如果存在就根据是否 PUBLIC ,来返回对应的构造函数的集合,如果集合不为空,返回,否则,继续执行后面代码。

判断你反射的是否是接口( Interface ),接口没有构造函数,所以会创建一个空数组返回,如果反射的不是接口,就通过 native 的方法 getDeclaredConstructors0 去获取。经过这些操作之后,把获得的构造函数的结合保存到 reflectionData 对象中,以便下次直接从内存中查找。

结果拿到之后,我们要从中找到一个匹配的构造方法,来创建实例。 arrayContentsEq 方法,通过比较你传入的参数集合(对应 newInstance 是空集合)和每一个 Constructor 对象中的参数集合,如果一致就会调用 Constructor copy 方法,返回一个 Constructor 对象。

继续返回 newInstance 中,上面 getConstructor0 结果返回之后,结果会保存到 cachedConstructor 变量,随后我们又看到了 AccessController.doPrivileged ,告诉系统,我可以访问其中的任何成员。接下来又开始检查,执行 Reflection.quickCheckMemberAccess

     public static boolean quickCheckMemberAccess(Class<?> var0, int var1) {
        return Modifier.isPublic(getClassAccessFlags(var0) & var1);
    }  

比较直接,就是检查是否包含 PUBLC ,如果不包含,就获取当前调用者(Caller),并和 newInstanceCallerCache 比对,如果不一样,就通过 Reflection.ensureMemberAccess 来验证,调用者是否能够访问成员,不能就抛出异常,能就把当前的 Caller 赋值给 newInstanceCallerCache

最后通过 Constructor newInstance 来创建实例。

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

override 变量就是前面Class的 newInstance AccessController.doPrivileged 设置的。

 java.security.AccessController.doPrivileged(
    new java.security.PrivilegedAction<Void>() {
        public Void run() {
                c.setAccessible(true);
                return null;
        }
});
 
public void setAccessible(boolean flag) throws SecurityException {
    ......
    setAccessible0(this, flag);
}
private static void setAccessible0(AccessibleObject obj, boolean flag)
        throws SecurityException
{
    ......
    obj.override = flag;
}  

随后,判断对象是否是枚举类型,如果是,抛出异常。

类型判断通过,就要构造一个构造函数访问器 ConstructorAccessor 。通过 ReflectionFactory newConstructorAccessor 方法创建。并通过访问器的 newInstance(NativeConstructorAccessorImpl类中) 实例化对象。来简单看一下相关代码。

 public ConstructorAccessor newConstructorAccessor(Constructor<?> var1) {
    //检查系统属性是否准备好
    checkInitted();
    Class var2 = var1.getDeclaringClass();
    //判断是否抽象类,是否Class类,是否匿名类等等
    if (Modifier.isAbstract(var2.getModifiers())) {
        return new InstantiationExceptionConstructorAccessorImpl((String)null);
    } else if (var2 == Class.class) {
        return new InstantiationExceptionConstructorAccessorImpl("Can not instantiate java.lang.Class");
    } else if (Reflection.isSubclassOf(var2, ConstructorAccessorImpl.class)) {
        return new BootstrapConstructorAccessorImpl(var1);
    } else if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
        return (new MethodAccessorGenerator()).generateConstructor(var1.getDeclaringClass(), var1.getParameterTypes(), var1.getExceptionTypes(), var1.getModifiers());
    } else {
        //创建NativeConstructorAccessorImpl对象,用于实例化对象
        NativeConstructorAccessorImpl var3 = new NativeConstructorAccessorImpl(var1);
        //初始化代理类,设置delegate变量为var3
        DelegatingConstructorAccessorImpl var4 = new DelegatingConstructorAccessorImpl(var3);
        var3.setParent(var4);
        return var4;
    }
}  

这样,整个对象实例化的过程就结束了。

我们的对象创建完后,我们就可以使用了。我们可以把反射创建的实例转化为具体的对象,访问其中的方法和属性。也可以继续通过反射执行里面的方法和读写属性。

文章来源:智云一二三科技

文章标题:Java:反射(newInstance)源码分析

文章地址:https://www.zhihuclub.com/179171.shtml

关于作者: 智云科技

热门文章

网站地图