java中的反射

作者: miaoLoveCode | 来源:发表于2017-07-10 19:00 被阅读598次

    反射,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实现
    getDeclaredField实现

    从源码可以看出:

    1. 校验成员变量是否允许被访问:
    • 允许被访问,跳转到步骤2;
    • 否则,抛出SecurityException异常;
    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实现

    从源码可以看出:

    • 缓存数据结构:ReflectionData,缓存了Field、Method和Constructor等相关数据;

    • 整个reflectionData方法,首先是从缓存中获取相关数据,需要注意的是,reflectionData对象是SoftReference类型的,该类型的数据在内存紧张时会被回收,如果该对象被回收,则通过newReflectionData方法重新创建一个新的对象,newReflectionData方法的相关源码实现如下:


      newReflectionData实现
    searchFields获取指定Field
    searchFields实现

    从源码可以看出,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实现
    getDeclaredMethod实现

    同getDeclaredField方法的流程一样:

    1. 校验方法是否允许被访问:
    • 允许被访问,跳转到步骤2;
    • 否则,抛出SecurityException异常;
    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实现

    从源码可以看出:

    • 如果root节点不为空,则从root节点获取MethodAccessor对象,并且将该对象赋值给当前Method对象的methodAccessor属性;

    • 如果root节点为null,或者root节点的methodAccessor对象为null时,调用reflectionFactory.newMethodAccessor方法为其创建一个新的MethodAccessor对象并返回。


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

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

    • ReflectionFactory重要属性

      ReflectionFactory相关属性
      ReflectionFactory有两个重要属性:noInflation(默认值为false)和inflationThreshold(默认值为15),它们的重新设置可以通过checkInitted方法来完成,源码如下图所示:
      checkInitted实现
      从源码标红框的两部分可以看出,noInflation和inflationThreshold可以通过参数-Dsun.reflect.inflationThreshold-Dsun.reflect.noInflation设置。
    • newMethodAccessor实现

      newMethodAccessor实现
      从源码可以看出:
      • 如果noInflation == true时,调用MethodAccessorGenerator对象的generateMethod方法生成MethodAccessorImpl对象;

      • 否则,生成DelegatingMethodAccessorImpl对象并返回。

    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的小伙伴可能都知道,类可以被回收只有在类的加载器可以被回收的情况下才会被回收,如果不每次生成新的类加载器,就可能会导致新创建的类一直不能被回收。

    相关文章

      网友评论

      • 2d7a87b0f412:人美文章写的更好, 不过 “searchFields获取指定Field” 这块有点突兀, 找了半天才知道 是讲到外层的方法调用了
      • 择夕_:头像真丑。。。

      本文标题:java中的反射

      本文链接:https://www.haomeiwen.com/subject/fpyqhxtx.html