
在《Java反射机制(一)—— 使用反射》一文中我们提到,在类的初始化阶段会创建对应类的一个Class对象,Class对象里还缓存了该类的所有Constructor对象、Method对象、Filed对象。这个初始化阶段是在JDK底层以C语言实现的,有兴趣可以自行查看。本文只对反射机制是如何使用Class对象实现实例创建、方法调用、属性访问进行说明。
1.反射创建对象的过程
1.1反射获取Constructor对象的过程
反射创建一个类的对象是从获取该类的Constructor(构造器)对象开始的。Class对象获取特定构造函数对象的方法是getDeclaredConstructor(Class<?>… parameterTypes) 。
JDK源码
/*Class*/
@CallerSensitive
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.DECLARED);
}
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int which) throws NoSuchMethodException
{
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
从上面两个方法不难看出,要获取目标Constructor对象分为两步,获取Class对象的所有构造器对象,根据参数类型数组获取到特定的构造器对象。
获取所有构造器对象的方法是privateGetDeclaredConstructors,在该方法中,会先试图获取Class对象在类初始化阶段就缓存的该类的所有Constructor对象,那这个缓存在什么位置呢,这个缓存就是Class的属性private volatile transient SoftReference<ReflectionData> reflectionData。这个属性的类型是SoftReference(软引用),所谓软引用就是在资源紧张的情况下GC会进行回收,这就可能导致缓存丢失。SoftReference的泛型是ReflectionData,ReflectionData就是缓存数据的真正格式。我们来看一下ReflectionData是怎么缓存数据的。
JDK源码
/*Class$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;
}
}
ReflectionData用数据的形式,将所有的Constructor、Method、Field对象存储下来供反射进行使用。
在新版本的JDK中对该缓存进行了优化,放弃使用ReflectionData存储,而是在Class中直接将Constructor、Method、Field数组放进软引用中作为缓存。这样就将缓存分散,当资源紧张时,不会一次全部被GC回收
了解了Class对象如何缓存Constructor,下面我们就来看privateGetDeclaredConstructors方法的源码。
JDK源码
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
checkInitted();
Constructor<T>[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
if (res != null) return res;
}
// No cached value available; request value from VM
if (isInterface()) {
@SuppressWarnings("unchecked")
Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
res = temporaryRes;
} else {
res = getDeclaredConstructors0(publicOnly);
}
if (rd != null) {
if (publicOnly) {
rd.publicConstructors = res;
} else {
rd.declaredConstructors = res;
}
}
return res;
}
上面我们说了软引用的缓存在资源紧张的情况下会被GC回收导致。所以在获取Constructor方法时也是分为两种情况,一种是缓存存在,一种是缓存为空。
- 当缓存存在的时候,JDK会直接从缓存中取出Constructor对象数组,需要注意的就是会根据publicOnly决定是取所有Constructor对象的数组还是仅取public方法的Constructor数据。由于在getConstructor0在调用privateGetDeclaredConstructors时已经传入了false值,所以此处是取所有Constructor对象的数组。
- 当缓存资源已被GC回收的时候,程序将向JVM请求重建该缓存。重建之后再将Constructor数据返回。
至此,已经获取到了Class对象的所有Constructor对象,下一步就需要根据参数类型数组来获取目标的Constructor对象。我们回到getDeclaredConstructor方法。
JDK源码
/*Class.getConstructor0*/
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
代码会遍历所有的Constructor对象,将其parameterTypes进行比对,当参数类型比对成功就返回该对象,若遍历结束也未找到目标构造器,则抛出NoSuchMethodException异常。
值得注意的是,这里不是直接返回缓存中的Constructor对象,而是利用ReflectionFactory复制了一个Constructor对象再返回。
调用ReflectionFactory对象的copyConstructor方法,在该方法中又使用LangReflectAccess接口的实现类ReflectAccess对象的copyConstructor方法最终对Constructor对象进行复制。
这句话有点绕,是因为JDK对该过程进行了层层封装。有意思的是,我们打开最终复制Constructor的方法,发现就是调用了Constructor对象自己的copy方法进行复制,然后返回复制的得到的对象。
JDK源码
/*ReflectAccess.copyConstructor*/
public <T> Constructor<T> copyConstructor(Constructor<T> arg) {
return arg.copy();
}
1.2利用Constructor对象创建实例
反射要创建一个实例,需要调用Constructor对象的newInstance方法。
JDK源码
/**Constructor.newInstance**/
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
在方法的第一段代码是对创建实例的权限进行验证,若在调用newInstance方法前已经设置了setAccess(true)则验证通过,否则将根据访问级别进行验证,若验证通过则程序继续往下执行,否则会抛出IllegalAccessException异常。
方法的第二段是在验证正在创建实例中的class是否为ENUM,因为枚举类是无法创建实例的,所以若为ENUM则抛出IllegalArgumentException异常。
在两次验证都通过后,方法的第三段代码将获取ConstructorAccessor进行实例创建。创建的实力的过程在NativeConstructorAccessorImpl的native方法newInstance0中。
2.反射调用方法
2.1获取目标方法对象
反射调用一个方法从获取Method对象开始。我们先看一下获取Method对象的getDeclaredMethod(String name, Class<?>… parameterTypes)方法源码。
JDK源码
/*Class*/
@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;
}
从这段代码我们可以看出,获取Method对象的语句是searchMethods(privateGetDeclaredMethods(false) , name , parameterTypes)。首先是调用privateGetDeclaredMethods(false)方法获取该Class对象的所有Method对象数组,然后调用searchMethods方法利用方法名和参数类型搜索Method对象数组获取目标Method对象,若最后没有获取到目标方法将抛出NoSuchMethodException异常。为了进一步了解这个过程,我们先来看一下privateGetDeclaredMethods方法的源码。
JDK源码
/*Class*/
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;
}
阅读这段代码会发现和获取所有构造器对象的方法privateGetDeclaredConstructors非常相似。因为他们实现原理是一样的,privateGetDeclaredMethods也是通过获取软引用缓存获取所有的Method对象。当缓存内容已被GC回收时,将请求JVM重建缓存。
获取到的所有Method对象组成的数组作为参数传入searchMethods中搜索获取目标的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));
}
在这个方法中,会根据传入的方法名称和参数列表与所有的Method进行匹配,若匹配到了则复制该Method返回,若未搜索到则返回null。
2.2 反射调用方法
在前文中介绍了获取Method对象的过程,下一步就是调用Method对象的invoke(Object obj, Object… args)方法调用该方法。我们知道在面向对象里,调用一个方法,离不开方法的调用者和参数列表。invoke方法的第一个参数就是目标方法的调用者对象,第二个参数就目标方法的参数列表参数类型组成的数组。
JDK源码
/*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调用方法也需要先对权限进行验证,若是Method对象设置了setAccess(true),则验证直接通过。否则需要验证方法调用者是否对目标方法有访问权限。
权限验证通过后会获取MethodAccessor对象,实际是调用MethodAccessor接口的底层实现类NativeMethodAccessor对象的invoke方法,其invoke方法又调用native方法invoke0最终实现对方法的调用。
3.反射访问Field
反射获取Field对象的方式与Constructor、Method一样,都是复制SoftReference缓存的Filed。获取Field值也是直接调用Field对象的get()方法。
需要重点说明就是对Filed值的修改。修改一个Field对象的值,需要通过调用对应FieldAccessor的set(Object obj, Object obj1)实现。而不同的Field有着不同的FieldAccessor。Field通过ReflectionFactory的newFieldAccessor方法创建一个FieldAccessor。而ReflecttionFactory调用**UnsafeFieldAccessorFactory.newFieldAccessor(field, flag)**最终实现了FieldAccessor的创建。
用一张图来表示UnsafeFieldAccessorFactory如何选择创建不同类型Field对应的Accessor。
- static属性: 普通静态Field创建的是UnsafeStaticXXXFieldAccessorImpl对象,该对象会在set操作时先获取Field的调用者对应的类的Class对象(该操作的主要针对调用Field对象的时候传入实例对象,而不是Class对象的情况),再将该Class对象传入native方法更改属性值。
- final static属性: 静态常量Field创建的是UnsafeQualifiedStaticXXXFieldAccessorImpl对象,该对象在创建时会给isReadOnly传值为true,所以当静态常量利用反射进行写操作时,将会抛出异常。
- 普通实例属性: 普通实例Field(即没有static也没有final修饰的属性)创建的是UnsafeXXXFieldAccessorImpl对象,该对象会直接调用底层native方法更改属性值。
- fianl属性: 常量Field创建的是UnsafeQualifiedXXXFieldAccessorImpl对象,该对象在创建时会给isReadOnly传值为false,所以常量利用反射进行写操作时是可以正常进行的,但最后会发现,调用set方法后,Field对应的值会被更改,但实际上实例的常量未被更改。
我们来看一下UnsafeFieldAccessorFactory的代码。
JDK源码
/*sun.reflect.UnsafeFieldAccessorFactory*/
class UnsafeFieldAccessorFactory
{
UnsafeFieldAccessorFactory()
{
}
static FieldAccessor newFieldAccessor(Field field, boolean flag)
{
Class class1 = field.getType();
boolean flag1 = Modifier.isStatic(field.getModifiers());
boolean flag2 = Modifier.isFinal(field.getModifiers());
boolean flag3 = Modifier.isVolatile(field.getModifiers());
boolean flag4 = flag2 || flag3;
boolean flag5 = flag2 && (flag1 || !flag);
if(flag1)
{
UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(field.getDeclaringClass());
if(!flag4)
{
if(class1 == Boolean.TYPE)
return new UnsafeStaticBooleanFieldAccessorImpl(field);
if(class1 == Byte.TYPE)
return new UnsafeStaticByteFieldAccessorImpl(field);
if(class1 == Short.TYPE)
return new UnsafeStaticShortFieldAccessorImpl(field);
if(class1 == Character.TYPE)
return new UnsafeStaticCharacterFieldAccessorImpl(field);
if(class1 == Integer.TYPE)
return new UnsafeStaticIntegerFieldAccessorImpl(field);
if(class1 == Long.TYPE)
return new UnsafeStaticLongFieldAccessorImpl(field);
if(class1 == Float.TYPE)
return new UnsafeStaticFloatFieldAccessorImpl(field);
if(class1 == Double.TYPE)
return new UnsafeStaticDoubleFieldAccessorImpl(field);
else
return new UnsafeStaticObjectFieldAccessorImpl(field);
}
if(class1 == Boolean.TYPE)
return new UnsafeQualifiedStaticBooleanFieldAccessorImpl(field, flag5);
if(class1 == Byte.TYPE)
return new UnsafeQualifiedStaticByteFieldAccessorImpl(field, flag5);
if(class1 == Short.TYPE)
return new UnsafeQualifiedStaticShortFieldAccessorImpl(field, flag5);
if(class1 == Character.TYPE)
return new UnsafeQualifiedStaticCharacterFieldAccessorImpl(field, flag5);
if(class1 == Integer.TYPE)
return new UnsafeQualifiedStaticIntegerFieldAccessorImpl(field, flag5);
if(class1 == Long.TYPE)
return new UnsafeQualifiedStaticLongFieldAccessorImpl(field, flag5);
if(class1 == Float.TYPE)
return new UnsafeQualifiedStaticFloatFieldAccessorImpl(field, flag5);
if(class1 == Double.TYPE)
return new UnsafeQualifiedStaticDoubleFieldAccessorImpl(field, flag5);
else
return new UnsafeQualifiedStaticObjectFieldAccessorImpl(field, flag5);
}
if(!flag4)
{
if(class1 == Boolean.TYPE)
return new UnsafeBooleanFieldAccessorImpl(field);
if(class1 == Byte.TYPE)
return new UnsafeByteFieldAccessorImpl(field);
if(class1 == Short.TYPE)
return new UnsafeShortFieldAccessorImpl(field);
if(class1 == Character.TYPE)
return new UnsafeCharacterFieldAccessorImpl(field);
if(class1 == Integer.TYPE)
return new UnsafeIntegerFieldAccessorImpl(field);
if(class1 == Long.TYPE)
return new UnsafeLongFieldAccessorImpl(field);
if(class1 == Float.TYPE)
return new UnsafeFloatFieldAccessorImpl(field);
if(class1 == Double.TYPE)
return new UnsafeDoubleFieldAccessorImpl(field);
else
return new UnsafeObjectFieldAccessorImpl(field);
}
if(class1 == Boolean.TYPE)
return new UnsafeQualifiedBooleanFieldAccessorImpl(field, flag5);
if(class1 == Byte.TYPE)
return new UnsafeQualifiedByteFieldAccessorImpl(field, flag5);
if(class1 == Short.TYPE)
return new UnsafeQualifiedShortFieldAccessorImpl(field, flag5);
if(class1 == Character.TYPE)
return new UnsafeQualifiedCharacterFieldAccessorImpl(field, flag5);
if(class1 == Integer.TYPE)
return new UnsafeQualifiedIntegerFieldAccessorImpl(field, flag5);
if(class1 == Long.TYPE)
return new UnsafeQualifiedLongFieldAccessorImpl(field, flag5);
if(class1 == Float.TYPE)
return new UnsafeQualifiedFloatFieldAccessorImpl(field, flag5);
if(class1 == Double.TYPE)
return new UnsafeQualifiedDoubleFieldAccessorImpl(field, flag5);
else
return new UnsafeQualifiedObjectFieldAccessorImpl(field, flag5);
}
}
看起来代码很长,但是我们把主要的逻辑提取出来发现其实并不复杂
static FieldAccessor newFieldAccessor(Field field, boolean flag)
{
Class class1 = field.getType();
boolean flag1 = Modifier.isStatic(field.getModifiers());//判断是否为static
boolean flag2 = Modifier.isFinal(field.getModifiers());//判断是否为fianl
boolean flag3 = Modifier.isVolatile(field.getModifiers());//判断是否为volatile
boolean flag4 = flag2 || flag3; //final或者volatile
boolean flag5 = flag2 && (flag1 || !flag);//判断是否为fianl static
if(flag1)
{
UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(field.getDeclaringClass());
if(!flag4) //static但非final
{
···
return new UnsafeStaticObjectFieldAccessorImpl(field);
}
//static final
···
return new UnsafeQualifiedStaticObjectFieldAccessorImpl(field, flag5);
}
if(!flag4) //非static非final
{
···
return new UnsafeObjectFieldAccessorImpl(field);
}
··· //final
return new UnsafeQualifiedObjectFieldAccessorImpl(field, flag5);
}
从以上简化后逻辑可以看出,静态常量和普通常量在创建Accessor时除了传入当前Field对象,还会传入flag5,这个flag5会对Accessor的isReadOnly进行赋值。如果仔细梳理你会发现,当创建静态常量Accessor时flag5永远是true。创建普通常量Accessor时,若Field未设置isAccess(true)则flag5为true,set方法将无法正常调用,若设置了isAccess(true)则flag5为false,set方法可以正常调用,但也只能进行“假写”操作。
~~~欢迎留言交流~~~
来源:CSDN
作者:温先森^_^
链接:https://blog.csdn.net/weixin_43231313/article/details/103516941