反射,java老司机应该都对它不陌生,不管是在jvm还是一些大型的框架中,总是能看到它的身影,接下来我们就来看看java的反射到底是如何实现的。
前言
在分析具体实现之前我们还是先来看个反射具体使用实例:
运行结果:
从案例可以看出,我们通过反射来获取对象定义的相关内容,同时还可以通过Method的invoke方法调用该对象任何方法。想想,如果在系统运行期间大量利用反射来操作对象,会不会有什么隐患呢?接下来我们就来看看java反射的具体实现。
Field的获取
java的反射可通过Field来获取某个类的属性或者属性值,Class类提供以下方法来获取Field:
public Field[] getDeclaredFields()
:获取类声明的所有属性,包括private属性;public Field getDeclaredField(String name)
:获取类声明的属性名为name的属性;public Field[] getFields()
:获取类声明的所有public类型属性;public Field getField(String name)
:获取类声明的属性名为name的public类型属性。
接下来我们就以getDeclaredField
方法为例,详细分析其源码实现。
getDeclaredField实现
从源码可以看出:
- 校验成员变量是否允许被访问:
- 允许被访问,跳转到步骤2;
- 否则,抛出SecurityException异常;
- 调用searchFields方法获取属性名为name的field;
- 如果步骤2的field为null,抛出NoSuchFieldException异常,否则,返回该field。
获取Field的逻辑主要在searchFields方法中实现,searchFields方法的第一个参数是通过privateGetDeclaredFields方法从缓存或者JVM获取到的Fields列表,接下来我们来看看它们的相关实现。
privateGetDeclaredFields获取Fields列表
从源码可以看出,privateGetDeclaredFields方法首先通过reflectionData方法从缓存中获取,如果从缓存中获取不到,再调用Reflection.filterFields方法从JVM中获取。接下来我们来看看reflectionData方法是如何实现数据缓存的。
reflectionData实现
从源码可以看出:
缓存数据结构:ReflectionData,缓存了Field、Method和Constructor等相关数据;
-
整个reflectionData方法,首先是从缓存中获取相关数据,需要注意的是,reflectionData对象是SoftReference类型的,该类型的数据在内存紧张时会被回收,如果该对象被回收,则通过newReflectionData方法重新创建一个新的对象,newReflectionData方法的相关源码实现如下:
searchFields获取指定Field
从源码可以看出,searchFields方法在找到指定的Field之后,会重新copy一份返回,当然如果没有找到指定Field则返回null。
到这里为止,Field相关获取的实现就告一段落,具体通过Field来操作对象相关部分源码在本文就不做详细讲解了,有兴趣的小伙伴可以自行阅读源码。接下来我们就继续来看看Method是如何来获取和调用的。
Method的获取
同Field一样,Class同样提供四个方法来获取Method:
public Method[] getDeclaredMethods()
:获取类的所有方法,包括private类型方法;public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
:获取类的指定方法名和参数的方法;public Method[] getMethods()
:获取类的所有public类型方法;public Method getMethod(String name, Class<?>... parameterTypes)
:获取类的指定方法名和参数的public类型方法。
接下来就以getDeclaredMethod
方法为例,详细分析Method的获取的具体实现。
getDeclaredMethod实现
同getDeclaredField方法的流程一样:
- 校验方法是否允许被访问:
- 允许被访问,跳转到步骤2;
- 否则,抛出SecurityException异常;
- 调用searchMethods方法获取指定方法名和参数的method;
- 如果步骤2的method为null,抛出NoSuchMethodException异常,否则,返回该method。
接下来我们就来看看searchMethods方法相关的具体实现。
注:由于searchMethods方法获取指定Method同searchFields方法获取指定Field实现基本一致,在这里就不在做详细的分析。
privateGetDeclaredMethods获取Method列表
同privateGetDeclaredFields方法一样,privateGetDeclaredMethods方法同样通过reflectionData方法从缓存中获取Method列表,如果从缓存到获取不到,才会调用Reflection.filterMethods方法从JVM中获取。
searchMethods获取指定Method
同searchFields方法一样,searchMethods方法在找到指定的Method之后,同样会重新copy一份返回。
到这里为止,Method的获取就简单分析完了,在获取到Method之后,我们可以通过其invoke方法来实现调用了,接下来我们就来看看invoke方法的具体实现。
Method的调用
从源码可以看出:
校验该方法是否允许被访问,允许被访问则跳转到步骤2,否则,抛出IllegalAccessException异常;
获取当前method的MethodAccessor对象ma,如ma为null,则调用acquireMethodAccessor方法获取;
调用MethodAccessor对象的invoke方法来实现调用,整个invoke方法的核心也在这里。
接下来我们就来看看acquireMethodAccessor方法获取MethodAccessor对象以及该对象的invoke方法的相关实现。
acquireMethodAccessor获取MethodAccessor
从源码可以看出:
如果root节点不为空,则从root节点获取MethodAccessor对象,并且将该对象赋值给当前Method对象的methodAccessor属性;
如果root节点为null,或者root节点的methodAccessor对象为null时,调用
reflectionFactory.newMethodAccessor
方法为其创建一个新的MethodAccessor对象并返回。
注:
从MethodAccessor的定义可以看出,MethodAccessor是一个接口,那么,上述MethodAccessor对象其实也就是MethodAccessor的子类的对象。
reflectionFactory.newMethodAccessor创建MethodAccessor
在开始分析newMethodAccessor方法的实现之前我们先来看看ReflectionFactory相关内容。
-
ReflectionFactory重要属性
ReflectionFactory有两个重要属性:noInflation(默认值为false)和inflationThreshold(默认值为15),它们的重新设置可以通过checkInitted方法来完成,源码如下图所示:
从源码标红框的两部分可以看出,noInflation和inflationThreshold可以通过参数-Dsun.reflect.inflationThreshold
和-Dsun.reflect.noInflation
设置。 -
newMethodAccessor实现
从源码可以看出:如果noInflation == true时,调用MethodAccessorGenerator对象的generateMethod方法生成MethodAccessorImpl对象;
否则,生成DelegatingMethodAccessorImpl对象并返回。
MethodAccessor.invoke方法实现
注:以子类DelegatingMethodAccessorImpl的具体实现为例,具体分析invoke方法的相关实现。
DelegatingMethodAccessorImpl实现
从源码可以看出,DelegatingMethodAccessorImpl对象就是一个代理对象,最终的invoke方法其实也就是调用NativeMethodAccessorImpl的invoke方法。
NativeMethodAccessorImpl实现
需要注意的是,如果当前invoke方法被调用的次数超过
ReflectionFactory.inflationThreshold
,后续的调用就通过MethodAccessorGenerator
对象的generateMethod
方法生成MethodAccessorImpl
对象,并将它赋值给delegate
,这样下次再调用Method.invoke
时,调用的也就是新生成的MethodAccessor
对象的invoke
方法。
generateMethod实现
注:由于generateMethod实现代码比较多,在这里我就不再贴源码了,有兴趣的小伙伴可以去openjdk看一下相关源码实现。
generateMethod在生成MethodAccessorImpl对象时,会生成相应的字节码并调用ClassDefiner.defineClass创建对应的对象。而每一次在调用ClassDefiner.defineClass创建对象时,都会生成一个类加载器,具体的源码如下图所示:
需要注意的是,为什么每次在创建对象时都需要生成类加载器呢?这么做的主要原因也是为了让这些生成的类可以被回收。稍微了解点儿gc的小伙伴可能都知道,类可以被回收只有在类的加载器可以被回收的情况下才会被回收,如果不每次生成新的类加载器,就可能会导致新创建的类一直不能被回收。