java中的反射

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 引言:java的高级特性-反射一直是困扰自己的一个很大问题,今天专门花了半天再将java中的反射看了一遍,下面简单...
    cp_insist阅读 2,337评论 1 6
  • 什么是反射 Java 反射是可以让我们在运行时获取类的函数、属性、父类、接口等 Class 内部信息的机制。 能做...
    红烧排骨饭阅读 548评论 0 1
  • 概述 Java 反射是可以让我们在运行时获取类的方法、属性、父类、接口等类的内部信息的机制。也就是说,反射本质上是...
    absfree阅读 6,265评论 3 15
  • 生活如戏 戏如生活 一直很想找一个这样历史故事 大概说的是: 从前,有一个能力不俗 忠心耿耿的副将,一直对他的将军...
    梅紫酱阅读 283评论 0 0
  • 怎么说 不管我再忙再不开心 也尽可能尽早回应 可是你在朋友圈点赞却不回微信的行为 我确实不知道怎么了
    牧七沙阅读 196评论 0 0