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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

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