前言
准备过年看下Spring源码,用来唬人,哈哈哈哈。正经点,是为了在遇到问题的时候,能知其然而知其所以然。但是在开始前,先恶补下基础知识。今天看框架之魂——反射。
反射的概述(基础部分开始)
反射是在编译状态,对某个类一无所知 ,但在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法。
这个说太干涩了,没有灵魂,就像下面两张图。
所以咱来举个例子,拒绝没有灵魂。O(∩_∩)O哈哈~
为什么要反射?
如果我们没有Orange类,那该类在编译的时候就会报错找不到该类。这是我们平常使用的“正射”。这个名字是为了和反射相对应,不是官方的术语。
但是这存在着一个明显的缺点,就是在main方法里调用的是Apple类,并没有调用Orange类,所以应该是可以正常调用的,当我想要调用Orange类的时候,再报错即可。但是,事与愿违,事情不是照着我们的想法而发展的。
我们需要一种在编译时不检查类的调用情况,只有在运行时,才根据相应的要求调用相应的类,这就是“反射”。
反射的用途
反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
举一个例子,在运用 Struts 2 框架的开发中我们一般会在 struts.xml
里去配置 Action
,比如:
<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute"> <result>/shop/shop-index.jsp</result> <result name="error">login.jsp</result> </action>
Action
建立了一种映射关系,当 View 层发出请求时,请求会被 StrutsPrepareAndExecuteFilter
拦截,然后 StrutsPrepareAndExecuteFilter
会去动态地创建 Action 实例。比如我们请求 login.action
,那么 StrutsPrepareAndExecuteFilter
就会去解析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。获取Class文件对象的三种方式
万事万物都是对象。
Apple apple=new Apple();
中的apple为Apple的一个实例。那Apple对象是哪个的实例呢?
其实是Class类的实例。
我们可以看他的注释,私有的构造方法,只有JVM才能创建对象。
如果我们能找到某个对象的Class类,即可以创建其实例。
- 静态属性class
- Object类的getClass方法,如果知道实例,直接调用其getClass方法。
- Class类的静态方法forName(),参数为类的完整路径
(推荐使用)
这里需要注意,通过类的全路径名获取Class对象会抛出一个异常,要用try....catch...捕获异常。如果根据类路径找不到这个类那么就会抛出这个异常,Class类中forName方法源码如下:
注:虽然写了三种方式,但平常使用最多,最推荐的是第三种方式,因为第一种方式需要知道类,第二种方式需要知道实例,如果知道了这些,可以直接调用其方法和参数,没必要再用Class来实现功能。举个例子,你从北京去上海,第一种方式直达就行,第二种方式和第三种方式则是先从北京到云南,再从云南到上海,显得太冗余。
反射的使用
我们以Apple类为例,利用发射来获取其参数和方法。其有三个参数,默认default参数color,公有public参数size,私有private参数price。三个构造方法,分别是默认default构造,公有public带有三个参数的有参构造,私有带有两个参数的有参构造。六个setter/getter方法公有方法,分别是color的默认default隔离级别的setter/getter方法,size的public隔离级别的setter/getter方法,price的private隔离级别的setter/getter方法。toString和三个参数的setter/getter方法。最后还有一个public隔离级别的toString方法。这样详细展开的描述,看起来很复杂,其实很简单的,具体代码如下:
package com.eastrobot.reflect; public class Apple extends Fruit{ String color;//默认default public int size; private int price; Apple() {//默认default System.out.println("Apple的无参构造"); } public Apple(String color, int size, int price) { this.color = color; this.size = size; this.price = price; System.out.println("Apple的有参构造——三个参数"); } private Apple(String color, int size) { this.color = color; this.size = size; this.price = 10; System.out.println("Apple的有参构造——两个参数"); } @Override public String toString() { return "color:" + color + ",size:" + size + ",price:" + price; } //默认default String getColor() { return color; } public int getSize() { return size; } private int getPrice() { return price; } //默认default void setColor(String color) { this.color = color; } public void setSize(int size) { this.size = size; } private void setPrice(int price) { this.price = price; } }
继承的父类Fruit,包括一个public类型的参数taste,和其public类型的setter/getter方法。
public class Fruit { public String taste; public String getTaste() { return taste; } public void setTaste(String taste) { this.taste = taste; } }
1.通过反射获取所有参数 getDeclaredFields
System.out.println("getDeclaredFields**********"); Field[] fields=appleClass.getDeclaredFields(); for(Field field:fields){ System.out.println(field.toString()); }
运行结果如下:
注:不管何种隔离级别,getDeclaredFields都会获取到所有参数。
2.通过反射获取指定参数getDeclaredField
//指定参数 System.out.println("getDeclaredField**********"); Field colorField=appleClass.getDeclaredField("color"); System.out.println("color:"+colorField.toString()); Field sizeField=appleClass.getDeclaredField("size"); System.out.println("size:"+sizeField.toString()); Field priceField=appleClass.getDeclaredField("price"); System.out.println("price:"+priceField.toString());
运行结果如下:
注:不管何种隔离级别,getDeclaredField可以通过输入值获取指定参数。
3.通过反射获取所有pubic类型的参数 getFields
System.out.println("getFields**********"); Field[] fields=appleClass.getFields(); for(Field field:fields){ System.out.println(field.toString()); }
运行结果如下:
注:只能通过反射获取public类型的属性,也包括继承自父类的属性。
4.通过反射获取指定public类型的参数 getField
Field colorField=appleClass.getField("color");
System.out.println("color:"+colorField.toString());
运行结果如下:
-------------------手动分割线-------------------
Field sizeField=appleClass.getField("size");
System.out.println("size:"+sizeField.toString());
运行结果如下:
-------------------手动分割线-------------------
Field priceField=appleClass.getField("price");
System.out.println("price:"+priceField.toString());
运行结果如下:
注:只有public类型才能通过getField方法获取到,其他类型均获取不到。
看到这里,有些小伙伴要问了,这是为啥,理由呢?咱不能死记硬背,这样过两天就忘了,记得不牢固,咱来瞅瞅底层干了啥。
插曲:为什么getFields和getField只能获取public类型的字段?
我们以getField为例,观察getDeclaredField和getField的区别,可以看到两者都调用了privateGetDeclaredFields方法,但是区别是getDeclaredField方法中的参数publicOnly是false,getField方法中的参数publicOnly为true。
getDeclaredField方法:
getField方法:
那privateGetDeclaredFields里面干了啥,我们看下。
我们可以看到如果为true,就取declaredPublicFields字段,即public字段,如果为false,就取DeclaredFields。
5.通过反射获取所有方法 getDeclaredMethods
//所有方法 System.out.println("getDeclaredMethods**********"); Method[] methods=appleClass.getDeclaredMethods(); for(Method method:methods){ System.out.println(method.toString()); }
运行结果如下:
6.通过反射获取指定方法 getDeclaredMethod
//指定方法 System.out.println("getDeclaredMethod**********"); //default Method getColorMethod=appleClass.getDeclaredMethod("getColor"); System.out.println("getColorMethod:"+getColorMethod.toString()); //public Method getSizeMethod=appleClass.getDeclaredMethod("getSize"); System.out.println("getSizeMethod:"+getSizeMethod.toString()); //private Method getPriceMethod=appleClass.getDeclaredMethod("getPrice"); System.out.println("getPriceMethod:"+getPriceMethod.toString()); //父类的public Method getTasteMethod=appleClass.getDeclaredMethod("getTaste"); System.out.println("getTasteMethod:"+getTasteMethod.toString());
运行结果如下:
注:getDeclaredMethod只能获取自己定义的方法,不能获取从父类的方法。
7.通过反射获取所有public类型的方法 getMethods
//所有方法 System.out.println("getMethods**********"); Method[] methods=appleClass.getMethods(); for(Method method:methods){ System.out.println(method.toString()); }
运行结果如下:
注:getMethods可以通过反射获取所有的public方法,包括父类的public方法。
8.通过反射获取指定public类型的方法 getMethod
//指定方法 System.out.println("getMethod**********"); Method method=appleClass.getMethod("toString"); System.out.println(method.toString());
运行结果如下:
9.通过反射获取所有构造方法 getDeclaredConstuctors
//构造方法 System.out.println("getDeclaredConstructors**********"); Constructor[] constructors=appleClass.getDeclaredConstructors(); for(Constructor constructor:constructors){ System.out.println(constructor.toString()); }
运行结果如下:
10.通过反射获取某个带参数的构造方法 getDeclaredConstructor
//指定构造方法 System.out.println("getDeclaredConstructor**********"); Class[] cArg = new Class[3]; cArg[0] = String.class; cArg[1] = int.class; cArg[2] = int.class; Constructor constructor=appleClass.getDeclaredConstructor(cArg); System.out.println(constructor.toString());
运行结果如下:
11.通过反射获取所有public类型的构造方法getConstructors
System.out.println("getConstructors**********"); Constructor[] constructors=appleClass.getConstructors(); for(Constructor constructor:constructors){ System.out.println(constructor.toString()); }
运行结果:
12.通过反射获取某个public类型的构造方法getConstructor
//构造方法 System.out.println("getConstructor**********"); Constructor constructor1 = appleClass.getConstructor(String.class,int.class,int.class); System.out.println("public类型的有参构造:" + constructor1.toString()); Constructor constructor2 = appleClass.getConstructor(String.class, int.class); System.out.println("private类型的有参构造:" + constructor2.toString());
运行结果:
13.通过无参构造来获取该类对象 newInstance()
//调用无参构造创建对象 Class appleClass = Class.forName("com.eastrobot.reflect.Apple"); Apple apple=(Apple)appleClass.newInstance();
运行结果如下:
14.通过有参构造来获取该类对象 newInstance(XXXX)
Constructor constructor=appleClass.getConstructor(String.class,int.class,int.class); Apple apple=(Apple)constructor.newInstance("红色",10,5);
运行结果如下:
15.获取类名包含包路径 getName()
String name= appleClass.getName();
System.out.println("name:"+name);
运行结果如下:
16.获取类名不包含包路径 getSimpleName()
String simpleName =appleClass.getSimpleName();
System.out.println("simpleName:"+simpleName);
运行结果如下:
17.通过反射调用方法 invoke
//调用无参构造创建对象 Class appleClass = Class.forName("com.eastrobot.reflect.Apple"); //调用有参构造 Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class); Apple apple = (Apple) constructor.newInstance("红色", 10, 20); //获取toString方法并调用 Method method = appleClass.getDeclaredMethod("toString"); String str=(String)method.invoke(apple); System.out.println(str);
注:invoke+实例可以调用相关public方法。
18.判断方法是否能调用isAccessible
//调用无参构造创建对象 Class appleClass = Class.forName("com.eastrobot.reflect.Apple"); //调用有参构造 Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class); Apple apple = (Apple) constructor.newInstance("红色", 10, 20); //获取public的getSize方法并调用 Method method = appleClass.getDeclaredMethod("getSize"); System.out.println("getSize方法的isAccessible:" + method.isAccessible()); int size = (Integer) method.invoke(apple); System.out.println("size:" + size); //获取private的getPrice方法并调用 method = appleClass.getDeclaredMethod("getPrice"); System.out.println("getPrice的isAccessible:" + method.isAccessible()); int price = (Integer) method.invoke(apple); System.out.println("price:" + price);
运行结果:
注:这样一看,public和private类型的isAccessible都为false,但是public类型的值可以获取到,但是private类型的值并不能获取到。其实isAccessible()值为 true或false,是指启用和禁用访问安全检查的开关,如果为true,则取消安全检查,为false,则执行安全检查。如上,两者都为false,说明两者的进行了安全检查,getSize为public类型,则可以获取值,而getPrice为private,则不能获取值。
19.设置安全检查开关setAccessible
//调用无参构造创建对象 Class appleClass = Class.forName("com.eastrobot.reflect.Apple"); //调用有参构造 Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class); Apple apple = (Apple) constructor.newInstance("红色", 10, 20); //获取price Method otherMethod = appleClass.getDeclaredMethod("getPrice"); System.out.println("getPrice方法的isAccessible:" + otherMethod.isAccessible()); otherMethod.setAccessible(true); int price = (Integer) otherMethod.invoke(apple); System.out.println("之前的price:" + price); //重新设置price Method method = appleClass.getDeclaredMethod("setPrice", int.class); System.out.println("isAccessible:" + method.isAccessible()); method.setAccessible(true); method.invoke(apple, 100); //再次获取price otherMethod = appleClass.getDeclaredMethod("getPrice"); otherMethod.setAccessible(true); price = (Integer) otherMethod.invoke(apple); System.out.println("之后的price:" + price);
运行结果:
注:setAccessible(true)表示取消安全检查,setAccessible(false)表示启用安全检查。
常见面试题解答(进阶部分开始)
被反射的类是否一定需要无参构造方法?
不一样。因为有参构造方法也可以反射,具体代码如下:
Constructor constructor=appleClass.getConstructor(String.class,int.class,int.class); Apple apple=(Apple)constructor.newInstance("红色",10,5);
反射的使用有什么优势和劣势?
优势:
在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息。反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
劣势:
使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码
。
使用反射会模糊程序内部逻辑
,程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。(这也就是看源码为什么这么难?哎。。。。)
为什么说反射可以降低耦合?
因为反射不是硬编码,在运行时可以灵活发现该类的详细信息,降低了代码之间的耦合性。
反射比较损耗性能,为什么这样说?(重点)
怎么去判断一个函数的性能?因为函数的执行太快太快了,你需要一个放慢镜,这样才能捕捉到他的速度。怎么做?把一个函数执行一百万遍或者一千万遍,你才能真正了解一个函数的性能。也就是,你如果想判断性能,你就不能还停留在秒级,毫秒级的概念。
如下是将直接获取实例,直接获取方法,反射获取实例,反射获取方法分别执行1百万次所花费差。
try { //直接获取实例 long startTime1 = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { new Apple(); } long endTime1 = System.currentTimeMillis(); System.out.println("直接获取实例时间:" + (endTime1 - startTime1)); //直接获取方法 long startTime2= System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { new Apple().toString(); } long endTime2 = System.currentTimeMillis(); System.out.println("直接获取方法时间:" + (endTime2- startTime2)); //反射获取实例 Class appleClass=Class.forName("com.eastrobot.reflect.Apple"); long startTime3 = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { appleClass.getDeclaredConstructor().newInstance(); } long endTime3 = System.currentTimeMillis(); System.out.println("反射获取实例:" + (endTime3 - startTime3)); //反射获取方法 Apple apple= (Apple)appleClass.getDeclaredConstructor().newInstance(); long startTime4 = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { Method method=appleClass.getMethod("toString"); method.invoke(apple); } long endTime4 = System.currentTimeMillis(); System.out.println("反射获取方法:" + (endTime4 - startTime4));
运行结果截图:
我们可以看到反射的确会导致性能问题,但反射导致的性能问题是否严重跟使用的次数有关系,如果控制在100次以内,基本上没什么差别,如果调用次数超过了100次,性能差异会很明显。
打个比方,如果快递员就在你住的小区,那么你报一个地址:xx栋xx号,那么快递员就可以马上知道你在哪里,直接就去到你家门口;但是,如果快递员是第一次来你们这里,他是不是首先得查查百度地图,看看怎么开车过去,然后到了小区是不是得先问问物管xx栋怎么找,然后,有可能转在楼下转了两个圈才到了你的门前。
我们看上面这个场景,如果快递员不熟悉你的小区,是不是会慢点,他的时间主要花费在了查找百度地图,询问物业管理。OK,反射也是一样,因为我事先什么都不知道,所以我得花时间查询一些其他资料,然后我才能找到你。
综上,大部分我们使用反射是不考虑性能的,平常使用的次数较少,如果真的遇到性能问题,如反射的效率影响到程序逻辑,可以采用缓存或Java字节码增强技术,参照库有asm,也有第三方工具库reflectAsm(https://github.com/EsotericSoftware/reflectasm)。
反射中的setAccessible()方法是否破坏了类的访问规则
setAccessible(true)取消了Java的权限控制检查(注意不是改变方法或字段的访问权限),对于setAccessible()方法是否会破坏类的访问规则,产生安全隐患,见下:
反射源码解析
我们跟进Method的invoke方法,分为两步,一是语言访问级别是否为重写,如果不是重写则调用Reflection的quickCheckMemberAccess方法,即通过Modifiers 判断是否具有访问权限,quickCheckMemberAccess方法主要是简单地判断 modifiers 是不是 public,如果不是的话就返回 false。所以 protected、private、default 修饰符都会返回 false,只有 public 都会返回 true。如果为false,则调用checkAccess方法。二是获取MethodAccessor对象,并调用其invoke方法。
public final class Method extends AccessibleObject implements GenericDeclaration, Member { private volatile MethodAccessor methodAccessor; //每个Java方法只有一个对应Method对象作为root,这个root不会暴露给用户, //而是每次通过反射获取Method对象时新创建的Method对象将root包装起来。 private Method root; @CallerSensitive public Object invoke(Object obj, Object... args)throws IllegalAccessException, IllegalArgumentException,InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor; //在第一次调用一个实际Java方法应该的Method对象的invoke方法之前 //实现调用逻辑的MethodAccessor对象还没有创建 //等到第一次调用时才创建MethodAccessor,并通过该MethodAccessor.invoke真正完成反射调用 if (ma == null) { ma = acquireMethodAccessor(); } //invoke并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理 return ma.invoke(obj, args); } ... private MethodAccessor acquireMethodAccessor() { MethodAccessor tmp = null; if (root != null) tmp = root.getMethodAccessor(); if (tmp != null) { methodAccessor = tmp; } else { //调用ReflectionFactory的newMethodAccessor方法,见下 tmp = reflectionFactory.newMethodAccessor(this); //更新root,以便下次直接使用 setMethodAccessor(tmp); } return tmp; } ... void setMethodAccessor(MethodAccessor accessor) { methodAccessor = accessor; // Propagate up if (root != null) { root.setMethodAccessor(accessor); } }
Reflection类:
public static boolean quickCheckMemberAccess(Class<?> memberClass, int modifiers) { return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers); }
ReflectionFactory类:
private static boolean noInflation = false; //选择java版还是C语言版的阈值 private static int inflationThreshold = 15; public MethodAccessor newMethodAccessor(Method var1) { checkInitted(); if (noInflation) { //java版 return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers()); } else { //c语言版 NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1); DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2); var2.setParent(var3); return var3; } }
如上述代码所示,实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。
Sun的JDK是从1.4系开始采用这种优化的,主要作者是Ken Russell
MethodAccessor的C语言实现(默认)
C语言版的MethodAccessor主要涉及这NativeMethodAccessorImpl和DelegatingMethodAccessorImpl两个类,而DelegatingMethodAccessorImpl是间接层,不是太重要,就不贴代码啦。以下是NativeMethodAccessorImpl的代码,核心是invoke方法:
class NativeMethodAccessorImpl extends MethodAccessorImpl { private final Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; NativeMethodAccessorImpl(Method var1) { this.method = var1; } public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { if (++this.numInvocations > ReflectionFactory.inflationThreshold()) { MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers()); this.parent.setDelegate(var3); } return invoke0(this.method, var1, var2); } void setParent(DelegatingMethodAccessorImpl var1) { this.parent = var1; } private static native Object invoke0(Method var0, Object var1, Object[] var2); }
每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。
MethodAccessor的Java实现
return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
Java的MethodAccessor主要涉及的是MethodAccessorGenerator类,具体代码超长,只截取了部分代码,主要有三个方法,直接就是上述的generateMethod方法,代码如下:
public MethodAccessor generateMethod(Class var1, String var2, Class[] var3, Class var4, Class[] var5, int var6) { return (MethodAccessor)this.generate(var1, var2, var3, var4, var5, var6, false, false, (Class)null); } private MagicAccessorImpl generate(final Class var1, String var2, Class[] var3, Class var4, Class[] var5, int var6, boolean var7, boolean var8, Class var9) { ByteVector var10 = ByteVectorFactory.create(); this.asm = new ClassFileAssembler(var10); this.declaringClass = var1; this.parameterTypes = var3; this.returnType = var4; this.modifiers = var6; this.isConstructor = var7; this.forSerialization = var8; this.asm.emitMagicAndVersion(); short var11 = 42; boolean var12 = this.usesPrimitiveTypes(); if (var12) { var11 = (short)(var11 + 72); } if (var8) { var11 = (short)(var11 + 2); } var11 += (short)(2 * this.numNonPrimitiveParameterTypes()); this.asm.emitShort(add(var11, (short)1)); final String var13 = generateName(var7, var8); this.asm.emitConstantPoolUTF8(var13); this.asm.emitConstantPoolClass(this.asm.cpi()); this.thisClass = this.asm.cpi(); if (var7) { if (var8) { this.asm.emitConstantPoolUTF8("sun/reflect/SerializationConstructorAccessorImpl"); } else { this.asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl"); } } else { this.asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl"); } this.asm.emitConstantPoolClass(this.asm.cpi()); this.superClass = this.asm.cpi(); this.asm.emitConstantPoolUTF8(getClassName(var1, false)); this.asm.emitConstantPoolClass(this.asm.cpi()); this.targetClass = this.asm.cpi(); short var14 = 0; if (var8) { this.asm.emitConstantPoolUTF8(getClassName(var9, false)); this.asm.emitConstantPoolClass(this.asm.cpi()); var14 = this.asm.cpi(); } this.asm.emitConstantPoolUTF8(var2); this.asm.emitConstantPoolUTF8(this.buildInternalSignature()); this.asm.emitConstantPoolNameAndType(sub(this.asm.cpi(), (short)1), this.asm.cpi()); if (this.isInterface()) { this.asm.emitConstantPoolInterfaceMethodref(this.targetClass, this.asm.cpi()); } else if (var8) { this.asm.emitConstantPoolMethodref(var14, this.asm.cpi()); } else { this.asm.emitConstantPoolMethodref(this.targetClass, this.asm.cpi()); } this.targetMethodRef = this.asm.cpi(); if (var7) { this.asm.emitConstantPoolUTF8("newInstance"); } else { this.asm.emitConstantPoolUTF8("invoke"); } this.invokeIdx = this.asm.cpi(); if (var7) { this.asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;"); } else { this.asm.emitConstantPoolUTF8("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); } this.invokeDescriptorIdx = this.asm.cpi(); this.nonPrimitiveParametersBaseIdx = add(this.asm.cpi(), (short)2); for(int var15 = 0; var15 < var3.length; ++var15) { Class var16 = var3[var15]; if (!isPrimitive(var16)) { this.asm.emitConstantPoolUTF8(getClassName(var16, false)); this.asm.emitConstantPoolClass(this.asm.cpi()); } } this.emitCommonConstantPoolEntries(); if (var12) { this.emitBoxingContantPoolEntries(); } if (this.asm.cpi() != var11) { throw new InternalError("Adjust this code (cpi = " + this.asm.cpi() + ", numCPEntries = " + var11 + ")"); } else { this.asm.emitShort((short)1); this.asm.emitShort(this.thisClass); this.asm.emitShort(this.superClass); this.asm.emitShort((short)0); this.asm.emitShort((short)0); this.asm.emitShort((short)2); this.emitConstructor(); this.emitInvoke(); this.asm.emitShort((short)0); var10.trim(); final byte[] var17 = var10.getData(); return (MagicAccessorImpl)AccessController.doPrivileged(new PrivilegedAction<MagicAccessorImpl>() { public MagicAccessorImpl run() { try { return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance(); } catch (InstantiationException var2) { throw (InternalError)(new InternalError()).initCause(var2); } catch (IllegalAccessException var3) { throw (InternalError)(new InternalError()).initCause(var3); } } }); } } private static synchronized String generateName(boolean var0, boolean var1) { int var2; if (var0) { if (var1) { var2 = ++serializationConstructorSymnum; return "sun/reflect/GeneratedSerializationConstructorAccessor" + var2; } else { var2 = ++constructorSymnum; return "sun/reflect/GeneratedConstructorAccessor" + var2; } } else { var2 = ++methodSymnum; return "sun/reflect/GeneratedMethodAccessor" + var2; } }
去阅读源码的话,可以看到MethodAccessorGenerator是如何一点点把Java版的MethodAccessor实现类生产出来的,其实就是一个逐步解析的过程。
此时要注意的是最后的“sun/reflect/GeneratedMethodAccessor”+var2的代码。
例子
以上空说无用,太干涩,咱来个例子。
public class Foo { public void foo(String name) { System.out.println("Hello, " + name); } }
public class test { public static void main(String[] args) { try { Class<?> clz = Class.forName("com.eastrobot.reflect.Foo"); Object o = clz.newInstance(); Method m = clz.getMethod("foo", String.class); for (int i = 0; i < 17; i++) { m.invoke(o, Integer.toString(i)); } } catch (Exception e) { } } }
除了上述代码,还需要在idea配置相关的运行参数,添加-XX:+TraceClassLoading参数,其为要求打印加载类的监控信息。
我们先用上述的例子执行下,运行结果如下,前面十五次是正常的,到第16次的时候,出现了很多打印信息,我已将一行标红,“GeneratedMethodAccessor1”,这其实就是上面说的Java版获取MethodAccessorGenerator的最后一行,1为自增参数。当第17次的时候,就不会用Java版的方式重新获取,而是直接复用啦。
结语
终于结束了,边玩边写,写了五天,累死了,答应我,一定要好好看,好吗?
如有说的不对地方,欢迎指正。