欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java 反射机制的学习-02

程序员文章站 2022-05-04 08:54:59
上接: Java 反射机制的学习-01Java反射机制的原理探究将对 forName、newInstance、getDeclaredField/getDeckaredMethod、setAccessible、set/get、invoke 等方法进行探究探究代码://com.duo1j.reflect02.Mainpublic class Main { public static void main(String[] args) { try { Cla...

上接: Java 反射机制的学习-01

Java反射机制的原理探究

将对 forNamenewInstancegetDeclaredField/getDeclaredMethodsetAccessibleset/getinvoke 等方法进行探究

探究代码:

//com.duo1j.reflect02.Main
public class Main {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("com.duo1j.reflect02.Test1");
            Object instance = clazz.newInstance();

            Field field = clazz.getDeclaredField("name");
            field.setAccessible(true);
            field.set(instance, "jack");

            Method method = clazz.getDeclaredMethod("hello");
            method.invoke(instance);

        } catch (Exception | Error e) {
            e.printStackTrace();
        }
    }
}
//----------------------------------------------------------------
//com.duo1j.reflect02.Test1
public class Test1 {

    private String name;

    public void hello() {
        System.out.println("Hello from " + name);
    }
}
//--------------------------------------------------------------
//Console
Hello from jack

一、Class.forName(String className)

@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

通过forName获取类信息主要调用forName0这个native方法交给jvm去处理
getClass() 直接调用了native方法
@CallerSensitive 注解表示该方法会根据调用者权限产生不同的处理
getCallerClass() 方法会返回调用者
获取类加载器后交给 native forName0 方法处理

//getCallerClass()
public class Main {

    public void test() {
        System.out.println(Reflection.getCallerClass(0));
        System.out.println(Reflection.getCallerClass(1));
        System.out.println(Reflection.getCallerClass(2));
        System.out.println(Reflection.getCallerClass(3));
    }
    
    public static void main(String[] args) {
        new Main().test();
    }
}
//Console
class sun.reflect.Reflection
class com.duo1j.reflect02.Main
class com.duo1j.reflect02.Main
null

之后jvm会调用ClassLoader类进行类加载

  1. 检查是否加载,已加载则直接返回
  2. 双亲委派加载,加载到后直接返回
  3. 仍未加载, 则自行加载后返回
//java.lang.ClassLoader#loadClass(java.lang.String, boolean)
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先,检查类是否已经被加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
            	//双亲委派机制加载类
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
            	//未找到该name的class异常
            }

            if (c == null) {
           	    //若双亲委派未找到,则自己处理
                long t1 = System.nanoTime();
                c = findClass(name);
                
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

二、newInstance()

  1. 权限检测
  2. 查找构造器,并缓存cachedConstructor
  3. 调用构造器,返回实例
//java.lang.Class#newInstance
@CallerSensitive
public T newInstance()
    throws InstantiationException, IllegalAccessException
{
	//进行权限检测
    if (System.getSecurityManager() != null) {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
    }

    // NOTE: the following code may not be strictly correct under
    // the current Java memory model.

    //查找构造方法
    //cachedConstructor 构造器缓存
    if (cachedConstructor == null) {
    	//限制Class类的newInstance方法
        if (this == Class.class) {
            throw new IllegalAccessException(
                "Can not call newInstance() on the Class for java.lang.Class"
            );
        }
        try {
            Class<?>[] empty = {};
            //获取构造器
            //getConstructor0会先获取所有构造器,然后根据参数匹配
            //匹配成功后,通过copyConstructor返回一份拷贝
            final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
            // Disable accessibility checks on the constructor
            // since we have to do the security check here anyway
            // (the stack depth is wrong for the Constructor's
            // security check to work)
            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;
    //权限检查
    int modifiers = tmpConstructor.getModifiers();
    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;
    }
}

三、getDeclaredField/getDeclaredMethod

这两者类似, 就选 getDeckaredMethod 来说一下

  1. 权限检测
  2. 通过privateGetDeclaredMethods获取方法列表
  3. searchMethods根据parameterTypes查找方法
@CallerSensitive
//java.lang.Class#getDeclaredMethod
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException 
    {
    	//权限检测
	    checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
	    //privateGetDeclaredMethods获取方法列表
	    //searchMethods根据parameterTypes查找对应方法
	    Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
	    if (method == null) {
	        throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
    }
    return method;
}
//java.lang.Class#privateGetDeclaredMethods
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;
    }
    //没有缓存,去虚拟机中获取
    res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
    if (rd != null) {
        if (publicOnly) {
            rd.declaredPublicMethods = res;
        } else {
            rd.declaredMethods = res;
        }
    }
    return res;
}

四、setAccessible(boolean flag)

//java.lang.reflect.AccessibleObject#setAccessible(boolean)
public void setAccessible(boolean flag) throws SecurityException {
	//权限检测
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
    //设置flag
    setAccessible0(this, flag);
}

private static void setAccessible0(AccessibleObject obj, boolean flag)
    throws SecurityException
{
	//禁止设置Class类
    if (obj instanceof Constructor && flag == true) {
        Constructor<?> c = (Constructor<?>)obj;
        if (c.getDeclaringClass() == Class.class) {
            throw new SecurityException("Cannot make a java.lang.Class" +
                                        " constructor accessible");
        }
    }
    //private boolean override
    obj.override = flag;
}

五、set/get()

set与get相似

//java.lang.reflect.Field#set
@CallerSensitive
public void set(Object obj, Object value)
    throws IllegalArgumentException, IllegalAccessException
{
	//private boolean override同上
    if (!override) {
    	//检查可访问性
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    //获取访问器 (FieldAccessor接口)
    getFieldAccessor(obj).set(obj, value);
}

六、invoke(Object obj, Object… args)

  1. 访问性检查
  2. 拿到methodAccessor (MethodAccessor接口声明invoke)
  3. 如果第一次调用没有methodAccessor,则由acquireMethodAccessor创建
  4. 调用invoke
//java.lang.reflect.Method#invoke
@CallerSensitive
public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException,
       InvocationTargetException
{
	//private boolean override同上
	//访问性检查
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    //获取methodAccessor (MethodAccessor 接口)
    MethodAccessor ma = methodAccessor; // read volatile
    //第一次调用没有methodAccessor, 则由acquireMethodAccessor创建
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

总结:

  1. 反射涉及很多native方法调用,反射获得的class信息存入relectionData作缓存,避免反复从jvm中读取。
  2. 反射是线程安全的。
  3. 反射获取方法、字段等都是进行匹配,因而类的大小会影响反射获取的效率。
  4. 查找到的方法、构造器、字段等会使用copyConstructor/copyMethod/copyField返回拷贝,保证数据隔离。

如有错误,恳请指正

本文地址:https://blog.csdn.net/weixin_44506615/article/details/107578196