介绍
反射是一种通过每个类的java.lang.Class对象来动态获取对应的类信息、创建对象以及调用方法的一种机制。
作用
1、在运行时判断任意一个对象所属的类;
2、在运行时构造任意一个类的对象;
3、在运行时判断任意一个类所具有的成员变量和方法;
4、在运行时调用任意一个对象的方法;生成动态代理。
反射的API
Java 类的成员包括以下三类:属性字段、构造函数、方法。反射的 API 也是与这几个成员相关:

- Field 类:提供有关类的属性信息,以及对它的动态访问权限。它是一个封装反射类的属性的类。
- Constructor 类:提供有关类的构造方法的信息,以及对它的动态访问权限。它是一个封装反射类的构造方法的类。
- Method 类:提供关于类的方法的信息,包括抽象方法。它是用来封装反射类方法的一个类。
- Class 类:表示正在运行的 Java 应用程序中的类的实例。
- Object 类:Object 是所有 Java 类的父类。所有对象都默认实现了 Object 类的方法。
源码分析
① 例子
import java.lang.reflect.Method;
public class ReflectCase {
public static void main(String[] args) throws Exception {
ReflectCase target = new ReflectCase();
Method method = ReflectCase.class.getDeclaredMethod("test");
method.invoke(target);
}
public void test() {
System.out.println("test");
}
}② getDeclaredMethod方法
//java.lang.Class
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}上面第5行的privateGetDeclaredMethods方法从缓存或JVM中获取该Class中申明的方法列表,searchMethods方法将从返回的方法列表里找到一个匹配名称和参数的方法对象。
③ searchMethods方法
下面我们进入上面第5行的searchMethods方法(该方法比较简单,不过有个知识点,就是每次都要拷贝一个新的Method对象,也就是说每次都要创建一个Method对象):
private static Method searchMethods(Method[] methods,
String name,
Class<?>[] parameterTypes)
{
Method res = null;
String internedName = name.intern();
//遍历methods数组,找到对应的method对象
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getName() == internedName
&& arrayContentsEq(parameterTypes, m.getParameterTypes())
&& (res == null
|| res.getReturnType().isAssignableFrom(m.getReturnType())))
res = m;
}
//只要传递正确的方法,就会由getReflectionFactory复制一个新的Method对象
return (res == null ? res : getReflectionFactory().copyMethod(res));
}④ copy方法:拷贝Method对象
//java.lang.reflect.Method
Method copy() {
// This routine enables sharing of MethodAccessor objects
// among Method objects which refer to the same underlying
// method in the VM. (All of this contortion is only necessary
// because of the "accessibility" bit in AccessibleObject,
// which implicitly requires that new java.lang.reflect
// objects be fabricated for each reflective call on Class
// objects.)
if (this.root != null)
throw new IllegalArgumentException("Can not copy a non-root Method");
Method res = new Method(clazz, name, parameterTypes, returnType,
exceptionTypes, modifiers, slot, signature,
annotations, parameterAnnotations, annotationDefault);
res.root = this;
// Might as well eagerly propagate this if already present
res.methodAccessor = methodAccessor;
return res;
}从上面的方法中,我们可以看到每次调用getDeclaredMethod方法返回的Method对象其实都是一个新的对象,且新对象的root属性都指向原来的Method对象。因此如果需要频繁调用,最好把Method对象缓存起来。
⑤ privateGetDeclaredMethods方法
下面我们进入上面代码段②中第5行的privateGetDeclaredMethods方法,该方法用于获取缓存或JVM中获取该Class中申明的方法列表。
//java.lang.Class
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData(); //从jvm或缓存中获取当前类的class对象的信息
if (rd != null) {
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
}
// No cached value available; request value from VM
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
}
return res;
}⑥ reflectionData方法
该方法用来获取ReflectionData对象。
//java.lang.Class
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
int classRedefinedCount = this.classRedefinedCount;
ReflectionData<T> rd;
if (useCaches &&
reflectionData != null &&
(rd = reflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
//如果没有缓存或者说软引用被回收
//或者该ReflectionData是旧的对象(应该是根据rd.redefinedCount == classRedefinedCount判断的),
//则会创建新的ReflectionData对象并放入该类的java.lang.Class对象中
return newReflectionData(reflectionData, classRedefinedCount);
}从reflectionData()方法实现可以看出:reflectionData对象是SoftReference类型的,说明在内存紧张时可能会被回收,不过也可以通过-XX:SoftRefLRUPolicyMSPerMB参数控制回收的时机,只要发生GC就会将其回收,如果reflectionData被回收之后,又执行了反射方法,那只能通过newReflectionData方法重新创建一个这样的对象了。
⑦ ReflectionData类
ReflectionData对象存储了java.lang.Class对象的基本信息。
//java.lang.Class
//该类在用户代码运行前会被调用,从而将对应的类信息缓存起来
// reflection data that might get invalidated when JVM TI RedefineClasses() is called
private static class ReflectionData<T> {
volatile Field[] declaredFields;
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] publicConstructors;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}⑧ method.invoke方法
调用method.invoke(target)后会进入该方法。
//java.lang.reflect.Method
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}应该注意到:这里的MethodAccessor对象是invoke方法实现的关键,一开始methodAccessor为空,需要调用acquireMethodAccessor生成一个新的MethodAccessor对象。MethodAccessor本身就是一个接口,它有
三个实现类。

调用过程如下图所示:

⑨ newMethodAccessor方法
在acquireMethodAccessor方法中,会通过ReflectionFactory类的newMethodAccessor创建一个实现了MethodAccessor接口的对象,实现如下:
//sun.reflect.ReflectionFactory
public MethodAccessor newMethodAccessor(Method method) {
checkInitted();
if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
return (new MethodAccessorGenerator())
.generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
} else {
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
acc.setParent(res);
return res;
}
}在ReflectionFactory类中,有2个重要的字段:noInflation(默认false)和inflationThreshold(默认15),在checkInitted方法中可以通过-Dsun.reflect.inflationThreshold=xxx和-Dsun.reflect.noInflation=true对这两个字段重新设置,而且只会设置一次.
如果noInflation为false,方法newMethodAccessor都会返回DelegatingMethodAccessorImpl对象;
反之,如果noInflation为true,比如在参数中设置-Dsun.reflect.noInflation=true,则每次都使用GeneratedMethodAccessorImpl对象(这个对象是在内存里新生成的专用Java类,它是Java版的MethodAccessor的实现类)。
⑩ NativeMethodAccessorImpl类
其实,DelegatingMethodAccessorImpl对象是一个代理对象,负责调用被代理对象delegate的invoke方法,其中delegate参数目前是NativeMethodAccessorImpl对象,所以最终Method的invoke方法调用的是NativeMethodAccessorImpl对象invoke方法,实现如下:
//sun.reflect.NativeMethodAccessorImpl
class NativeMethodAccessorImpl extends MethodAccessorImpl {
private Method method;
private DelegatingMethodAccessorImpl parent;
private int numInvocations;
NativeMethodAccessorImpl(Method method) {
this.method = method;
}
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
if (++numInvocations > ReflectionFactory.inflationThreshold()
&& !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
MethodAccessorImpl acc = (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(acc);
}
return invoke0(this.method, obj, args);
}这里用到了ReflectionFactory类中的inflationThreshold,当delegate调用了15次invoke方法之后,如果继续调用则通过MethodAccessorGenerator类的generateMethod方法生成MethodAccessorImpl对象,并设置为delegate对象,这样下次执行Method.invoke时,就调用新建的MethodAccessor对象的invoke()方法了。
也就是说,每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。
⑪ defineClass方法
generateMethod方法在生成MethodAccessorImpl对象时,会在内存中生成对应的字节码,并调用ClassDefiner.defineClass创建对应的class对象。
//sun.reflect.ClassDefiner
static Class<?> defineClass(String name, byte[] bytes, int off, int len,
final ClassLoader parentClassLoader) {
ClassLoader newLoader = (ClassLoader)AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return new DelegatingClassLoader(parentClassLoader);
}
});
return unsafe.defineClass(name, bytes, off, len, newLoader, null);
}这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载,从其设计来看本身就不希望这些类一直存在内存里的,在需要的时候有就行了。
⑫ GeneratedMethodAccessor生成过程代码
该方法用来在内存里生成新的专用Java类的名称。在invoke调用超过15次时使用的MethodAccessor的名字就是下面的这个GeneratedMethodAccessor。
private static synchronized String generateName(boolean isConstructor,
boolean forSerialization)
{
if (isConstructor) {
if (forSerialization) {
int num = ++serializationConstructorSymnum;
return "sun/reflect/GeneratedSerializationConstructorAccessor" + num;
} else {
int num = ++constructorSymnum;
return "sun/reflect/GeneratedConstructorAccessor" + num;
}
} else {
int num = ++methodSymnum;
return "sun/reflect/GeneratedMethodAccessor" + num;
}
}
问题
① 为什么要有两个MethodAccessor实现版本?
答:实际的 MethodAccessor 实现有两个版本,一个是 Native 版本NativeMethodAccessorImpl,一个是 Java 版本MethodAccessorImpl。Java版本在第一次启动时,需要加载字节码实现Method.invoke() 和Constructor.newInstance() ,比较耗时。所以Native 版本第一次启动比java版本快3-4倍,但是后面的调用java版本比Native快20倍。所以为了避免启动慢,第一次使用native版本快速启动。因此为了避免后续运行慢,会在调用了15次invoke方法之后再切换到java版本MethodAccessorImpl 对象去实现反射。

总结
- 反射的开销随着版本的升级不断降低,但还是比直接调用的开销要大。
当该反射调用成为热点时,它甚至可以被内联到靠近Method.invoke()的一侧,从而大大降低了反射调用的开销。而native版的反射调用则无法被有效内联,因而调用开销无法随程序的运行而降低。
虽说Sun的JDK这种实现方式使得反射调用方法成本比以前降低了很多,但Method.invoke()本身要用数组包装参数;而且每次调用都必须检查方法的可见性(在Method.invoke()里),也必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0()里或者生成的Java版本的MethodAccessor.invoke()里);而且Method.invoke()就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得Method.invoke()自身难以被内联到调用方。
相比之下JDK7里新的MethodHandler则更有潜力,在其功能完全实现后能达到比普通反射调用方法更高的性能。在使用MethodHandle来做反射调用时,MethodHandle.invoke()的形式参数与返回值类型都是准确的,所以只需要在链接方法的时候才需要检查类型的匹配性,而不必在每次调用时都检查。而且MethodHandle是不可变值,在创建后其内部状态就不会再改变了;JVM可以利用这个知识而放心的对它做激进优化,例如将实际的调用目标内联到做反射调用的一侧。
- 每次使用反射来调用方法时,都会重新创建Method对象,因此,建议对Method对象进行缓存。
- 反射的底层通过ReflectionData对象对每个类的基本信息进行缓存。ReflectionData对象被软引用所关联,因此当要gc时,该对象会被回收。
- 反射中通过生成新的ClassLoader来及时卸载那些不希望一直存储在内存里的类。
- MethodAccessor(可以理解为Method调用器)实现有两个版本,一个是 Native 版本NativeMethodAccessorImpl,一个是 Java 版本MethodAccessorImpl。Java版本在第一次启动时,需要加载字节码实现Method.invoke() 和Constructor.newInstance() ,比较耗时。所以Native 版本第一次启动比java版本快3-4倍,但是后面的调用java版本比Native快20倍。所以为了避免启动慢,第一次使用native版本快速启动。因此为了避免后续运行慢,会在调用了15次invoke方法之后再切换到java版本MethodAccessorImpl 对象去实现反射。
- 个人感觉反射的原理不难,无非就是通过java.lang.Class对象动态获取类信息,然后去调用类中的方法;我认为在源码中比较复杂、或者说篇幅比较大的部分在于对反射操作的优化,比如通过ReflectionData对类信息进行缓存,而不用去方法区获取;当多次调用native方法时,会生成Java版的方法,这样可以避免多次调用native方法,从而提高调用效率。