反射底层原理
反射作用
首先回顾一下反射的作用,反射可以根据一个类的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实际是一个代理NativeMethodAccessor的DelegatingMethodAccessor(实际调用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);
}
854

被折叠的 条评论
为什么被折叠?



