乐趣区

关于后端:反射原理解析

1 背景

前段时间组内针对“拷贝实例属性是应该用 BeanUtils.copyProperties()还是 MapStruct”这个问题进行了一次强烈的 battle。反对 MapStruct 的同学给出了他厌弃 BeanUtils 的理由:因为用了反射,所以慢。这个理由一下子拉回了我边远的记忆,在我刚开始理解反射这个 Java 个性的时候,简直看到的每一篇文章都会有“Java 反射不能频繁应用”、“反射影响性能”之类的话语,过后只是当一个论断记下了这些话,却没有深究过为什么,所以正好借此机会来探索一下 Java 反射的代码。

2 反射

包构造梳理反射相干的代码次要在 jdk rt.jar 下的 java.lang.reflect 包下,还有一些相干类在其余包门路下,这里先按下不表。依照继承和实现的关系先简略划分下 java.lang.reflect 包:① Constructor、Method、Field 三个类型别离能够形容实例的构造方法、一般办法和字段。三种类型都间接或间接继承了 AccessibleObject 这个类型,此类型里次要定义两种办法,一种是通用的、对拜访权限进行解决的办法,第二种是可供继承重写的、与注解相干的办法。

② 只看选中的五种类型,咱们平时所用到的一般类型,譬如 Integer、String,又或者是咱们自定义的类型,都能够用 Class 类型的实例来示意。Java 引入泛型之后,在 JDK1.5 中裁减了其余四种类型,用于泛型的示意。别离是 ParameterizedType(参数化类型)、WildcardType(通配符类型)、TypeVariable(类型变量)、GenericArrayType(泛型数组)。

③ 与②中形容的五种根本类型对应,下图这五个接口 / 类别离用来示意五种根本类型的注解相干数据。

④ 下图为实现动静代理的相干类与接口。java.lang.reflect.Proxy 次要是利用反射的一些办法获取代理类的类对象,获取其构造方法,由此结构出一个实例。java.lang.reflect.InvocationHandler 是代理类须要实现的接口,由代理类实现接口内的 invoke 办法,此办法会负责代理流程和被代理流程的执行程序组织。

3 指标

类实例的结构源码以 String 类的对象实例化为例,看一下反射是如何进行对象实例化的。Class<?> clz = Class.forName("java.lang.String");

String s =(String)clz.newInstance();
Class 对象的结构由 native 办法实现,以 java.lang.String 类为例,先看看结构好的 Class 对象都有哪些属性:

能够看到目前只有 name 一个属性有值,其余属性临时都是 null 或者默认值的状态。下图是 clz.newInstance() 办法逻辑的流程图,接下来对其中次要的两个办法进行阐明:

从上图能够看出整个流程有两个外围局部。因为通常状况下,对象的结构都须要依附类里的构造方法来实现,所以第一局部就是拿到指标类对应的 Constructor 对象;第二局部就是利用 Constructor 对象,结构指标类的实例。3.1 获取 Constructor 对象首先上一张 Constructor 对象的属性图:

java.lang.Class#getConstructor0 此办法中次要做的工作是首先拿到指标类的 Constructor 实例数组 (次要由 native 办法实现),数组里每一个对象都代表了指标类的一个构造方法。而后对数组进行遍历,依据办法入参提供的 parameterTypes, 找到合乎的 Constructor 对象,而后从新发明一个 Constructor 对象,属性值与原 Constructor 统一(称为正本 Constructor),并且正本 Constructor 的属性 root 指向源 Constructor,相当于对源 Constructor 对象进行了一层封装。因为在 getConstructor0() 办法将返回值返回给调用方之后,调用方在后续的流程里进行了 constructor.setAccesssible(true)的操作,这个办法的作用是敞开对 constructor 这个对象拜访时的 Java 语言拜访查看。语言拜访查看是个耗时的操作,所以正当猜想是为了进步反射性能敞开了这个查看,又出于平安思考,所以将最原始的对象进行了封装。

private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                    int which) throws NoSuchMethodException
{
//1、拿到 Constructor 实例数组并进行筛选
    Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
 //2、通过对入参的比拟筛选出符合条件的 Constructor
    for (Constructor<T> constructor : constructors) {
        if (arrayContentsEq(parameterTypes,
                            constructor.getParameterTypes())) {
//3、创立正本 Constructor
            return getReflectionFactory().copyConstructor(constructor);
        }
    }
    throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
退出移动版