前言
在上篇 Java 反射机制(一) 介绍了一些 Java
反射相关的常用 API ,在知道了如何去使用反射之后,作为一个合格的工程师,下一步肯定是要去了解它的如何实现的,我们今天就来看看在 JDK
源码中是如何去实现反射的(PS:以下源码分析基于 JDK1.8
)。
Field 类 set 方法的实现
Field
类的 set
方法是在运行时用来动态修改一个类的属性的值,进入到 Field
类的 set
方法的源码如下:
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
getFieldAccessor(obj).set(obj, value);
}
首先根据 override
判断是否需要检查字段的访问权限,然后通过 getFieldAccessor
方法获得一个 FieldAccessor
字段访问者对象,最后调用的是 FieldAccessor
类的 set
方法进行下一步操作的,FieldAccessor
是一个接口,定义了对字段的一些操作,该接口有如下一些实现类:
fieldaccessor_implements.png
要看 set
到底调用的是哪个实现类的方法,那么我们需要看看 getFieldAccessor()
返回的是哪个类的对象,下面是 getFieldAccessor
方法的源码实现:
// security check is done before calling this method
private FieldAccessor getFieldAccessor(Object obj)
throws IllegalAccessException
{
boolean ov = override;
FieldAccessor a = (ov) ? overrideFieldAccessor : fieldAccessor;
return (a != null) ? a : acquireFieldAccessor(ov);
}
这里先通过 override
来获取不同的缓存的 FieldAccessor
,其中 overrideFieldAccessor
代表本类覆盖父类的字段访问者对象缓存,fieldAccessor
是本类的字段访问器对象缓存。如果缓存存在的话就直接复用之前的对象,否则就调用 Field
类的 acquireFieldAccessor
方法获取。我们进入到 acquireFieldAccessor
方法中看看,方法源码如下:
private FieldAccessor acquireFieldAccessor(boolean overrideFinalCheck) {
// First check to see if one has been created yet, and take it
// if so
FieldAccessor tmp = null;
if (root != null) tmp = root.getFieldAccessor(overrideFinalCheck);
if (tmp != null) {
if (overrideFinalCheck)
overrideFieldAccessor = tmp;
else
fieldAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newFieldAccessor(this, overrideFinalCheck);
setFieldAccessor(tmp, overrideFinalCheck);
}
return tmp;
}
从 acquireFieldAccessor
的源码中我们可以看到,先判断是否已存在 FieldAccessor
对象,如果存在的话那么就会复用之前的 FieldAccessor
对象,否则就使用 reflectionFactory
工厂的 newFieldAccessor
方法生成一个新的 FieldAccessor
对象出来。所以我们就要进到 newFieldAccessor
方法里面看看是如何生成的,方法源码如下:
public FieldAccessor newFieldAccessor(Field var1, boolean var2) {
checkInitted();
return UnsafeFieldAccessorFactory.newFieldAccessor(var1, var2);
}
从 newFieldAccessor
方法代码可以得知,在方法里面是通过 UnsafeFieldAccessorFactory
类的 static
方法 newFieldAccessor
来生产 FieldAccessor
的,那么我们继续进入到 UnsafeFieldAccessorFactory
类的 newFieldAccessor
方法里面看看,方法源码如下:
static FieldAccessor newFieldAccessor(Field var0, boolean var1) {
Class var2 = var0.getType();
boolean var3 = Modifier.isStatic(var0.getModifiers());
boolean var4 = Modifier.isFinal(var0.getModifiers());
boolean var5 = Modifier.isVolatile(var0.getModifiers());
boolean var6 = var4 || var5;
boolean var7 = var4 && (var3 || !var1);
if (var3) {
UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(var0.getDeclaringClass());
if (!var6) {
if (var2 == Boolean.TYPE) {
return new UnsafeStaticBooleanFieldAccessorImpl(var0);
} else if (var2 == Byte.TYPE) {
return new UnsafeStaticByteFieldAccessorImpl(var0);
} else if (var2 == Short.TYPE) {
return new UnsafeStaticShortFieldAccessorImpl(var0);
} else if (var2 == Character.TYPE) {
return new UnsafeStaticCharacterFieldAccessorImpl(var0);
} else if (var2 == Integer.TYPE) {
return new UnsafeStaticIntegerFieldAccessorImpl(var0);
} else if (var2 == Long.TYPE) {
return new UnsafeStaticLongFieldAccessorImpl(var0);
} else if (var2 == Float.TYPE) {
return new UnsafeStaticFloatFieldAccessorImpl(var0);
} else {
return (FieldAccessor)(var2 == Double.TYPE ? new UnsafeStaticDoubleFieldAccessorImpl(var0) : new UnsafeStaticObjectFieldAccessorImpl(var0));
}
}
// 剩下的部分省略...
}
}
从以上 UnsafeFieldAccessorFactory
类的 newFieldAccessor
方法代码可以看出,方法里面通过类的字段修饰符类型和字段的类类型共同决定返回的 FieldAccessor
实现类,这里要注意一下方法里面这几个变量的含义:
- var3(isStatic):静态属性,也就是
static
关键字修饰的属性。 - var4(isFinal):
final
关键字修饰的属性。 - var5(isVolatile):
valatile
关键字修饰的属性。 - var6(isQualified):
valatile
关键字或者final
关键字修饰的属性。 - var7 (isReadOnly):是否只读属性,
final
关键字修饰的属性或者static
关键字修饰并且不能覆盖(override = false
)的属性
举一个例子,假设在一个类中的字段声明为 public static String name
,那么返回的字段访问器为 UnsafeStaticCharacterFieldAccessorImpl
,我们看看这个类的 set
方法是如何实现的,方法源码如下:
public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
if (this.isFinal) {
this.throwFinalFieldIllegalAccessException(var2);
}
if (var2 == null) {
this.throwSetIllegalArgumentException(var2);
}
if (var2 instanceof Character) {
unsafe.putChar(this.base, this.fieldOffset, (Character)var2);
} else {
this.throwSetIllegalArgumentException(var2);
}
}
从上面方法的代码得知,方法最终还是通过 Unsafe
类的 native
方法 putChar(Object var1, long var2, char var4)
来实现的,有关 Unsafe
类的介绍请看这篇文章(Java魔法类:Unsafe应用解析)。
Method 类 invoke 方法的实现
Method
类的 invoke
方法用来在运行时动态调用对象的方法,我们进入到 Method
类的 invoke
方法中看看在 JDK
中到底是怎么做的,方法源码如下:
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);
}
从以上方法代码我们可以看到,和上文说的的 Field
类一样,首先也是先根据 override
进行了一些权限检查,最后调用的是 MethodAccessor
的 invoke
方法进行处理,这个方法访问器 MethodAccessor
是一个接口,它只有一个操作方法调用的 invoke
方法,它有如下三个实现类:
要想知道 ma.invoke
具体调用的是哪个类的方法,我们需要看看方法 acquireMethodAccessor
返回的对象是哪个,该方法的源码如下:
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;
}
从以上方法 acquireMethodAccessor
的源码可以看出,首先会先先判断是否已经存在了对应的 MethodAccessor
对象,如果有就会复用这个对象,否则就调用工厂 reflectionFactory
的 newMethodAccessor
方法生成一个 MethodAccessor
对象出来。那么我们就需要进入到方法 newMethodAccessor
中,方法源码如下:
public MethodAccessor newMethodAccessor(Method var1) {
checkInitted();
if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
} else {
NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
var2.setParent(var3);
return var3;
}
}
从方法 newMethodAccessor
的代码可以看到,方法首先是使用 Method
对象作为入参生成了 NativeMethodAccessorImpl
对象,然后再使用 NativeMethodAccessorImpl
对象作为入参生成了 DelegatingMethodAccessorImpl
对象。这个使用了代理模式
,将 NativeMethodAccessorImpl
交给了 DelegatingMethodAccessorImpl
类进行了代理,进入到代理类 DelegatingMethodAccessorImpl
中可以看到:
从上面的红色方框可以看到,在类 DelegatingMethodAccessorImpl
的构造方法中将参数赋值给类中的 delegate
属性,所有上所说的 ma.invoke
最终会进入到 DelegatingMethodAccessorImpl
代理类的 invoke
,方法里调用的是 delegate
属性的 invoke
方法,该属性声明的类型为抽象类 MethodAccessorImpl
,它有如下两个实现类:
按照上文所说的,这里的 delegate
属性是 NativeMethodAccessorImpl
对象,那么就进入到 NativeMethodAccessorImpl
的 invoke
方法中,方法源码如下:
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
this.parent.setDelegate(var3);
}
return invoke0(this.method, var1, var2);
}
类 NativeMethodAccessorImpl
的 invoke
方法会先判断此次调用是否超过 ReflectionFactory.inflationThreshold()
方法返回的阈值(PS:默认的阈值大小为 15
),如果超过了该阈值,则使用方法访问生成器重新生成一个 MethodAccessorImpl
,并将 DelegatingMethodAccessorImpl
的 delegate
属性指向这个新生成的 MethodAccessorImpl
对象。从 Reflectionfactory
工厂类的一下注释:
可以得知 JVM
初次加载字节码实现反射的时候,使用 Method.invoke
和 Constructor.newInstance
方式加载所花费的时间是使用原生代码加载所花费的时间的 3 - 4 倍。这也就是我们平常说为什么频繁使用反射的应用需要花费更多的时间。JVM
作者们为了避免这种花费较长的加载时间,我们在第一次加载的时候重用了 JVM
的入口,之后切换到字节码实现的实现。
正如注释所述,在 MethodAccessor
接口的实现中,有两个不同的版本,一个 Java
实现的,一个是 Native
实现的。Java
版本实现的版本在初始化的时需要比较多的时间,但长久来说性能会更好一些;而 Native
版本则正好相反,在初始化时相对较快,但是在运行一段时间之后性能就不如 Java
版本的了。为了权衡两种版本的特性,sun
公司的 JDK
使用了 inflation
机制,让 Java
方法在被反射调用时,开头的几次调用使用 native
版,等反射调用次数超过阈值时则生成一个专用的 MethodAccessor
实现类,生成其中的 invoke
方法的字节码,以后对该 Java
方法的反射调用就会使用 Java
版。
总结
本文主要介绍反射调用 set(Object obj, Object value)
方法和 invoke(Object obj, Object... args)
方法的底层实现,由于水平有限本人暂时还没有能力分析 JVM
的实现,这里只分析到最终 native
方法的调用。底层会依赖到 Unsafe
类来执行一些低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升 Java
运行效率、增强 Java
语言底层资源操作能力方面起到了很大的作用。对于属性反射的方法 setXXX
和 getXXX
的实现分别对应 Unsafe
类的 putXXX
和 getXXX
方法,也就是说完全依赖 Unsafe
类中的 native
方法来实现的;对于方法反射的方法 invoke
底层调用的是 NativeMethodAccessorImpl
类的 invoke0
的 native
方法来实现的。对于反射构造器调用的实现,读者可以自己进入其源码进行分析,大体上和反射方法调用的实现类似。
参考文章
网友评论