Java 反射机制(二)

前言

在上篇 Java 反射机制(一) 介绍了一些 Java 反射相关的常用 API ,在知道了如何去使用反射之后,作为一个合格的工程师,下一步肯定是要去了解它的如何实现的,我们今天就来看看在 JDK 源码中是如何去实现反射的(PS:以下源码分析基于 JDK1.8)。

Field 类 set 方法的实现

Field 类的 set 方法是在运行时用来动态修改一个类的属性的值,进入到 Field 类的 set 方法的源码如下:

1
2
3
4
5
6
7
8
9
10
11
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 方法的源码实现:

1
2
3
4
5
6
7
8
// 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 方法中看看,方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 方法里面看看是如何生成的,方法源码如下:

1
2
3
4
public FieldAccessor newFieldAccessor(Field var1, boolean var2) {
checkInitted();
return UnsafeFieldAccessorFactory.newFieldAccessor(var1, var2);
}

newFieldAccessor 方法代码可以得知,在方法里面是通过 UnsafeFieldAccessorFactory 类的 static 方法 newFieldAccessor 来生产 FieldAccessor 的,那么我们继续进入到 UnsafeFieldAccessorFactory 类的 newFieldAccessor 方法里面看看,方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 方法是如何实现的,方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 中到底是怎么做的,方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 进行了一些权限检查,最后调用的是 MethodAccessorinvoke 方法进行处理,这个方法访问器 MethodAccessor 是一个接口,它只有一个操作方法调用的 invoke 方法,它有如下三个实现类:

methodaccessor_implements.png

要想知道 ma.invoke 具体调用的是哪个类的方法,我们需要看看方法 acquireMethodAccessor 返回的对象是哪个,该方法的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 对象,如果有就会复用这个对象,否则就调用工厂 reflectionFactorynewMethodAccessor 方法生成一个 MethodAccessor 对象出来。那么我们就需要进入到方法 newMethodAccessor 中,方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
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.png

从上面的红色方框可以看到,在类 DelegatingMethodAccessorImpl 的构造方法中将参数赋值给类中的 delegate 属性,所有上所说的 ma.invoke 最终会进入到 DelegatingMethodAccessorImpl 代理类的 invoke,方法里调用的是 delegate 属性的 invoke 方法,该属性声明的类型为抽象类 MethodAccessorImpl,它有如下两个实现类:

methodaccessorimpl_implements.png

按照上文所说的,这里的 delegate 属性是 NativeMethodAccessorImpl 对象,那么就进入到 NativeMethodAccessorImplinvoke 方法中,方法源码如下:

1
2
3
4
5
6
7
8
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);
}

NativeMethodAccessorImplinvoke 方法会先判断此次调用是否超过 ReflectionFactory.inflationThreshold() 方法返回的阈值(PS:默认的阈值大小为 15),如果超过了该阈值,则使用方法访问生成器重新生成一个 MethodAccessorImpl,并将 DelegatingMethodAccessorImpldelegate 属性指向这个新生成的 MethodAccessorImpl 对象。从 Reflectionfactory 工厂类的一下注释:

reflectionfactory_doc.png

可以得知 JVM 初次加载字节码实现反射的时候,使用 Method.invokeConstructor.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 语言底层资源操作能力方面起到了很大的作用。对于属性反射的方法 setXXXgetXXX 的实现分别对应 Unsafe 类的 putXXXgetXXX 方法,也就是说完全依赖 Unsafe 类中的 native 方法来实现的;对于方法反射的方法 invoke 底层调用的是 NativeMethodAccessorImpl 类的 invoke0native 方法来实现的。对于反射构造器调用的实现,读者可以自己进入其源码进行分析,大体上和反射方法调用的实现类似。


参考文章

-------------本文结束感谢您的阅读-------------
mghio wechat
微信公众号「mghio」
赏作者☕️