反射底层原理

反射底层原理

反射作用

首先回顾一下反射的作用,反射可以根据一个类的Class对象获得该类的Field、Constructor、Method对象,并对这三类对象进行操作。

反射获得Class对象的方法有三种:

  • 已知类名:Clazz.class
  • 已知全限定名:Class.forName(“xx.xx.Clazz”)
  • 已知对象:instance.getClass()

反射底层实现原理

反射的缓存

利用Class对象从JVM获得的Field、Constructor、Method对象的存储定义在Class类中的静态内部类ReflectionData<T>中;Class对象持有一个ReflectData<T>软引用实例:

private volatile transient SoftReference<ReflectionData<T>> 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;
    }
}

由于这个属性是软引用的,在某些内存比较苛刻的情况下是可能被回收的,也可以通过-XX:SoftRefLRUPolicyMSPerMB这个参数来控制回收的时机。

反射获取的方式

以获取类的Method为例:

  • 获取所有方法

获取所有方法会调用getDeclaredMethods方法:

@CallerSensitive
public Method[] getDeclaredMethods() throws SecurityException {
    checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
    return copyMethods(privateGetDeclaredMethods(false));
}

该方法会先检查是否允许访问,默认策略是允许的,然后会调用privateGetDeclaredMethods(boolean publicOnly)去进行寻找该类的方法,传入的boolean参数决定是否只获取定义为public的方法:

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

该方法中可以看到会首先检查reflectionData中是否已经缓存了结果;

如果缓存中没有,再通过getDeclaredMethods0()从虚拟机去获取,该方法是一个native方法。

最后根据privateGetDeclaredMethods方法返回的Method数组,调用copyMethods()复制一份相同的Method数组进行返回:

private static Method[] copyMethods(Method[] arg) {
    Method[] out = new Method[arg.length];
    ReflectionFactory fact = getReflectionFactory();
    for (int i = 0; i < arg.length; i++) {
        out[i] = fact.copyMethod(arg[i]);
    }
    return out;
}
  • 获取某个方法

获取某个方法会调用searchMethods方法从上面提到的privateGetDeclaredMethods返回的Method数组里找到一个同名的匹配的方法:

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

searchMethods方法并不会直接返回匹配的Method对象,而是利用反射工厂(ReflectionFactory)去复制一个新的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));
}

反射工厂的copyMethod()方法的具体实现是Method类中的copy()方法:

Method copy() {
    if (this.root != null)
        throw new IllegalArgumentException("Can not copy a non-root Method");

    Method res = new Method(clazz, name, parameterTypes, returnType,
                            exceptionTypes, modifiers, slot, signature,
                            annotations, parameterAnnotations, annotationDefault);
    res.root = this;
    // Might as well eagerly propagate this if already present
    res.methodAccessor = methodAccessor;
    return res;
}

由此可见,我们每次通过调用getDeclaredMethod方法返回的Method对象其实都是一个新的对象。

利用反射进行方法的调用

获得了Method对象之后,就可以通过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)实际是通过MethodAccessor进行的;MethodAccessor接口定义了invoke方法:

public interface MethodAccessor {
    Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException;
}

MethodAccessor有三个实现:

  • NativeMethodAccessorImpl:使用native方法去调用方法;

  • MethodAccessorImpl:抽象类,并没有提供invoke方法的具体实现;

  • DelegatingMethodAccessorImpl:继承了MethodAccessorImpl,是一个代理,可以通过构造函数或setter方法设置具体的实现;其invoke方法的实现是调用其代理实现的invoke:

    class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
        private MethodAccessorImpl delegate;
    
        DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {
            this.setDelegate(var1);
        }
    
        public Object invoke(Object var1, Object[] var2) 
            throws IllegalArgumentException, InvocationTargetException {
            return this.delegate.invoke(var1, var2);
        }
    
        void setDelegate(MethodAccessorImpl var1) {
            this.delegate = var1;
        }
    }
    

那么为什么要设计DelegatingMethodAccessorImpl这样一个代理呢?

继续往下看,会根据方法的调用次数切换MethodAccessor的具体实现,利用DelegatingMethodAccessorImpl这样一个代理的setter方法,就可以轻松切换。

至于为什么要切换,也留到后面再说。

那么具体是使用哪个MethodAccessor来实现方法的调用的呢?

回到上面的Method.invoke()方法中,会调用acquireMethodAccessor方法去获取MethodAccessor,我们来详细分析获取的过程:

private MethodAccessor acquireMethodAccessor() {
    // First check to see if one has been created yet, and take it
    // if so
    MethodAccessor tmp = null;
    if (root != null) tmp = root.getMethodAccessor();
    if (tmp != null) {
        methodAccessor = tmp;
    } else {
        // Otherwise fabricate one and propagate it up to the root
        tmp = reflectionFactory.newMethodAccessor(this);
        setMethodAccessor(tmp);
    }

    return tmp;
}

这个方法很简单,首先查询缓存(Method类的一个字段);如果没有获取过,就调用反射工厂的newMethodAccessor方法去创建一个,并存到对象对应的字段中去;

我们来看newMethodAccessor方法:

public MethodAccessor newMethodAccessor(Method var1) {
    checkInitted();
    if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
        // 利用字节码生成一个MethodAccessor
        return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
    } else {
        // 使用NativeMethodAccessor
        NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
        DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
        var2.setParent(var3);
        return var3;
    }
}

checkInitted方法我们留到后面再讲,首先看生成的MethodAccessor,会有两种可能:

  • 如果noInflation参数为true(默认为false,后面再介绍具体含义),另一个参数不去管它:会由MethodAccessorGenerator根据字节码相当于生成一个虚拟的Class对象来调用其相应的方法;(这种方法性能会好很多,相当于直接调用一个类的某个方法
  • 否则,MethodAccessor实际是一个代理NativeMethodAccessorDelegatingMethodAccessor(实际调用native方法的invoke)。

根据上面的分析,可以得知实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native实现的。它们两个的性能各有优劣:Java实现的版本会在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。

那么如何权衡两种实现呢?根据逻辑我们也可以轻松想到,就是通过一个参数来统计使用次数:如果偶尔使用,我们就使用native版本;如果经常使用,就用jdk版本。

jdk中关于这个参数有两个设置,上面newMethodAccessor方法中我们还没具体介绍的checkInitted方法就是去检查这个参数的,该参数有两种设置:

  • -Dsun.reflect.inflationThreshold=xxx:默认15,表示超过15次使用JDK版本的MethodAccessor。
  • -Dsun.reflect.noInflation=true:直接不使用native版本的;我们从newMethodAccessor方法中也可以看到如果noInflation为true就直接创建JDK版本的。

根据默认的noInflation为false,会在15次调用之前使用NatvieMethodAccessor,在该类中会对调用次数进行统计,当超过15次时,就会调用代理(DelegatingMethodAccerssorImpl)的setter方法切换成JDK版本的,这也就是之前提到的这个代理的作用,具体请看下面代码:

class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private final Method method;
    private DelegatingMethodAccessorImpl parent;
    private int numInvocations;

    NativeMethodAccessorImpl(Method var1) {
        this.method = var1;
    }

    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        // 每次调用invoke之前先计数
        if (++this.numInvocations > ReflectionFactory.inflationThreshold() 
            && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
            // 如果超过了inflationThreshold设置的值,就会切换成JDK版本的MethodAccessor实现
            MethodAccessorImpl var3 = (MethodAccessorImpl)
                (new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
            // 利用代理的setter方法实现切换
            this.parent.setDelegate(var3);
        }
		// 使用native版本的invoke
        return invoke0(this.method, var1, var2);
    }

    void setParent(DelegatingMethodAccessorImpl var1) {
        this.parent = var1;
    }

    private static native Object invoke0(Method var0, Object var1, Object[] var2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值