Java的反射机制
概念
Java反射机制是指程序可以在运行时,(动态)加载和使用编译期间未指定的类。
具体过程是,通过类名获取类的完整构造(如属性、方法,可用于反编译),并声称类的对象,并调用其方法。
优点:代码更简洁、提高了灵活性和扩展性
缺点:性能差、破坏封装性
使用场景:反编译、简单工厂模式、IOC、AOP(动态代理)
反射常用API
获取类对象的方法
1.Class A = Class.forName(该类的全路径名,如java.lang.String)
2.调用目标类的class属性,只适合在编译前就知道要操作的 Class。
Class A = String.class;
3.调用对象的getClass()方法
String str = new String("Hello");
Class A = str.getClass();
通过反射创建类对象
1.通过 Class 对象的 newInstance()
Class clz = Apple.class;
Apple apple = (Apple)clz.newInstance();
2.通过 Constructor 对象的 newInstance() 方法
Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();
通过反射获取类属性、方法、构造器
通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性
Class clz = Apple.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性
Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
反射的原理和源码分析
本节内容参考:https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
反射中最终用的就是 Method 类的 invoke 方法
MethodAccessor 是一个接口,定义了方法调用的具体操作,而它有三个具体的实现类:
- sun.reflect.DelegatingMethodAccessorImpl
- sun.reflect.MethodAccessorImpl
- sun.reflect.NativeMethodAccessorImpl
ma.invoke() 到底调用的是哪个类的 invoke 方法,则需要看看 MethodAccessor 对象返回的到底是哪个类对象,这需要看acquireMethodAccessor() 方法
其过程是先判断是否存在对应的 MethodAccessor 对象,如果存在那么就复用之前的 MethodAccessor 对象,否则调用 ReflectionFactory 对象的 newMethodAccessor 方法生成一个 MethodAccessor 对象
ReflectionFactory 类的 newMethodAccessor 方法里,我们可以看到首先是生成了一个 NativeMethodAccessorImpl 对象,再这个对象作为参数调用 DelegatingMethodAccessorImpl 类的构造方法。这里的实现是使用了代理模式,将 NativeMethodAccessorImpl 对象交给 DelegatingMethodAccessorImpl 对象代理。查看DelegatingMethodAccessorImpl 类的构造方法可以知道,其实是将 NativeMethodAccessorImpl 对象赋值给 DelegatingMethodAccessorImpl 类的 delegate 属性。
所以说ReflectionFactory 类的 newMethodAccessor 方法最终返回 DelegatingMethodAccessorImpl 类对象。所以我们在前面的 ma.invoke() 里,其将会进入 DelegatingMethodAccessorImpl 类的 invoke 方法中。
进入 DelegatingMethodAccessorImpl 类的 invoke 方法后,这里调用了 delegate 属性的 invoke 方法,它又有两个实现类,分别是:DelegatingMethodAccessorImpl 和 NativeMethodAccessorImpl。按照我们前面说到的,这里的 delegate 其实是一个 NativeMethodAccessorImpl 对象,所以这里会进入 NativeMethodAccessorImpl 的 invoke 方法。
NativeMethodAccessorImpl 的 invoke 方法里,其会判断调用次数是否超过阀值(numInvocations)。如果超过该阀值,那么就会生成另一个MethodAccessor 对象,并将原来 DelegatingMethodAccessorImpl 对象中的 delegate 属性指向最新的 MethodAccessor 对象。
到此,我们可以知道 MethodAccessor 对象其实就是具体去生成反射类的入口。实际的 MethodAccessor 实现有两个版本,一个是 Native 版本,一个是 Java 版本。Native 版本一开始启动快,但是随着运行时间边长,速度变慢。Java 版本一开始加载慢,但是随着运行时间边长,速度变快。
所以第一次加载的时候我们会发现使用的是 NativeMethodAccessorImpl 的实现,而当反射调用次数超过 15 次之后,则使用 MethodAccessorGenerator 生成的 MethodAccessorImpl 对象去实现反射。
Method 类的 invoke 方法整个流程可以表示成如下的时序图:
总结
原来 invoke 方法内部有两种实现方式,一种是 native 原生的实现方式,一种是 Java 实现方式,这两种各有千秋。而为了最大化性能优势,JDK 源码使用了代理的设计模式去实现最大化性能。