小毛同学做笔记之Java进阶测试分享

java中的反射

2017-07-10  本文已影响598人  miaoLoveCode

反射,java老司机应该都对它不陌生,不管是在jvm还是一些大型的框架中,总是能看到它的身影,接下来我们就来看看java的反射到底是如何实现的。

前言

在分析具体实现之前我们还是先来看个反射具体使用实例:


使用案例

运行结果:


运行结果
从案例可以看出,我们通过反射来获取对象定义的相关内容,同时还可以通过Method的invoke方法调用该对象任何方法。想想,如果在系统运行期间大量利用反射来操作对象,会不会有什么隐患呢?接下来我们就来看看java反射的具体实现。

Field的获取

java的反射可通过Field来获取某个类的属性或者属性值,Class类提供以下方法来获取Field:

接下来我们就以getDeclaredField方法为例,详细分析其源码实现。

getDeclaredField实现
getDeclaredField实现

从源码可以看出:

  1. 校验成员变量是否允许被访问:
  1. 调用searchFields方法获取属性名为name的field;
  2. 如果步骤2的field为null,抛出NoSuchFieldException异常,否则,返回该field。

获取Field的逻辑主要在searchFields方法中实现,searchFields方法的第一个参数是通过privateGetDeclaredFields方法从缓存或者JVM获取到的Fields列表,接下来我们来看看它们的相关实现。

privateGetDeclaredFields获取Fields列表
privateGetDeclaredFields实现

从源码可以看出,privateGetDeclaredFields方法首先通过reflectionData方法从缓存中获取,如果从缓存中获取不到,再调用Reflection.filterFields方法从JVM中获取。接下来我们来看看reflectionData方法是如何实现数据缓存的。

reflectionData实现
reflectionData实现

从源码可以看出:

searchFields获取指定Field
searchFields实现

从源码可以看出,searchFields方法在找到指定的Field之后,会重新copy一份返回,当然如果没有找到指定Field则返回null。

到这里为止,Field相关获取的实现就告一段落,具体通过Field来操作对象相关部分源码在本文就不做详细讲解了,有兴趣的小伙伴可以自行阅读源码。接下来我们就继续来看看Method是如何来获取和调用的。

Method的获取

同Field一样,Class同样提供四个方法来获取Method:

接下来就以getDeclaredMethod方法为例,详细分析Method的获取的具体实现。

getDeclaredMethod实现
getDeclaredMethod实现

同getDeclaredField方法的流程一样:

  1. 校验方法是否允许被访问:
  1. 调用searchMethods方法获取指定方法名和参数的method;
  2. 如果步骤2的method为null,抛出NoSuchMethodException异常,否则,返回该method。

接下来我们就来看看searchMethods方法相关的具体实现。

:由于searchMethods方法获取指定Method同searchFields方法获取指定Field实现基本一致,在这里就不在做详细的分析。

privateGetDeclaredMethods获取Method列表
privateGetDeclaredMethods实现

同privateGetDeclaredFields方法一样,privateGetDeclaredMethods方法同样通过reflectionData方法从缓存中获取Method列表,如果从缓存到获取不到,才会调用Reflection.filterMethods方法从JVM中获取。

searchMethods获取指定Method
searchMethods实现

同searchFields方法一样,searchMethods方法在找到指定的Method之后,同样会重新copy一份返回。

到这里为止,Method的获取就简单分析完了,在获取到Method之后,我们可以通过其invoke方法来实现调用了,接下来我们就来看看invoke方法的具体实现。

Method的调用

invoke实现

从源码可以看出:

  1. 校验该方法是否允许被访问,允许被访问则跳转到步骤2,否则,抛出IllegalAccessException异常;

  2. 获取当前method的MethodAccessor对象ma,如ma为null,则调用acquireMethodAccessor方法获取;

  3. 调用MethodAccessor对象的invoke方法来实现调用,整个invoke方法的核心也在这里。

接下来我们就来看看acquireMethodAccessor方法获取MethodAccessor对象以及该对象的invoke方法的相关实现。

acquireMethodAccessor获取MethodAccessor
acquireMethodAccessor实现

从源码可以看出:


MethodAccessor定义
从MethodAccessor的定义可以看出,MethodAccessor是一个接口,那么,上述MethodAccessor对象其实也就是MethodAccessor的子类的对象。
reflectionFactory.newMethodAccessor创建MethodAccessor

在开始分析newMethodAccessor方法的实现之前我们先来看看ReflectionFactory相关内容。

MethodAccessor.invoke方法实现

:以子类DelegatingMethodAccessorImpl的具体实现为例,具体分析invoke方法的相关实现。

DelegatingMethodAccessorImpl实现

DelegatingMethodAccessorImpl实现
从源码可以看出,DelegatingMethodAccessorImpl对象就是一个代理对象,最终的invoke方法其实也就是调用NativeMethodAccessorImpl的invoke方法。

NativeMethodAccessorImpl实现

NativeMethodAccessorImpl实现
需要注意的是,如果当前invoke方法被调用的次数超过 ReflectionFactory.inflationThreshold,后续的调用就通过MethodAccessorGenerator对象的generateMethod方法生成MethodAccessorImpl对象,并将它赋值给delegate,这样下次再调用Method.invoke时,调用的也就是新生成的MethodAccessor对象的invoke方法。

generateMethod实现

注:由于generateMethod实现代码比较多,在这里我就不再贴源码了,有兴趣的小伙伴可以去openjdk看一下相关源码实现。

generateMethod在生成MethodAccessorImpl对象时,会生成相应的字节码并调用ClassDefiner.defineClass创建对应的对象。而每一次在调用ClassDefiner.defineClass创建对象时,都会生成一个类加载器,具体的源码如下图所示:


defineClass实现

需要注意的是,为什么每次在创建对象时都需要生成类加载器呢?这么做的主要原因也是为了让这些生成的类可以被回收。稍微了解点儿gc的小伙伴可能都知道,类可以被回收只有在类的加载器可以被回收的情况下才会被回收,如果不每次生成新的类加载器,就可能会导致新创建的类一直不能被回收。

上一篇下一篇

猜你喜欢

热点阅读