万字总结之反射(框架之魂)

前言

准备过年看下Spring源码,用来唬人,哈哈哈哈。正经点,是为了在遇到问题的时候,能知其然而知其因此然。可是在开始前,先恶补下基础知识。今天看框架之魂——反射。html

反射的概述(基础部分开始)

反射是在编译状态,对某个类一无所知 ,但在运行状态中,对于任意一个类,都能知道这个类的全部属性和方法。java

这个说太干涩了,没有灵魂,就像下面两张图。git

因此咱来举个例子,拒绝没有灵魂。O(∩_∩)O哈哈~github

为何要反射?

若是咱们没有Orange类,那该类在编译的时候就会报错找不到该类。这是咱们日常使用的“正射”。这个名字是为了和反射相对应,不是官方的术语。面试

可是这存在着一个明显的缺点,就是在main方法里调用的是Apple类,并无调用Orange类,因此应该是能够正常调用的,当我想要调用Orange类的时候,再报错便可。可是,事与愿违,事情不是照着咱们的想法而发展的。缓存

咱们须要一种在编译时不检查类的调用状况,只有在运行时,才根据相应的要求调用相应的类,这就是“反射”。安全

反射的用途

反射最重要的用途就是开发各类通用框架。不少框架(好比 Spring)都是配置化的(好比经过 XML 文件配置 Bean),为了保证框架的通用性,它们可能须要根据配置文件加载不一样的对象或类,调用不一样的方法,这个时候就必须用到反射,运行时动态加载须要加载的对象。app

举一个例子,在运用 Struts 2 框架的开发中咱们通常会在struts.xml里去配置Action,好比:框架

<action name="login"jsp

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版的方式从新获取,而是直接复用啦。

结语

终于结束了,边玩边写,写了五天,累死了,答应我,必定要好好看,好吗?

若有说的不对地方,欢迎指正。

求个关注

欢迎关注,咱一块儿学习,么么哒。

参考资料

Java反射详细介绍

Java反射中getDeclaredField和getField的区别

java反射的使用场合和做用、及其优缺点

反射是否真的会让你的程序性能下降?

深刻解析Java反射(1) - 基础

关于反射调用方法的一个log

反射进阶,编写反射代码值得注意的诸多细节

相关文章
相关标签/搜索