今天我们来通过源代码,看看 Java 中反射是如何工作的。
反射,其实在我们日常的编程过程中可能用的并不多,但是第三方的库,这个技术确实应用非常广泛的,包括我们在开发 Android 应用时,有时为了操作一些不开放的系统属性时,也会用到反射。
这里我们将通过分析反射的运行,来看看 Class 这个类的功能。开始之前,先提到两个静态的整型变量,在 java.lang.reflect.Member 中。这两个变量后面会经常提到。
/**
* 所有的public成员,包括继承关系和接口
*/
PUBLIC static final int PUBLIC = 0;
/**
* 所有访问级别的成员,不包括继承(父类、子类)及接口
*/
public static final int DECLARED = 1;
再看几个属性,在内部类 ReflectionData 中,保存着一些反射获取的方法、字段等信息集合。
//存放自己的所有字段,不包括继承关系的
volatile Field[] declaredFields;
//存放包括继承关系的public的字段
volatile Field[] publicFields;
//存放自己的,不包括继承关系的方法
volatile Method[] declaredMethods;
//存放包括继承关系的public的方法
volatile Method[] publicMethods;
//自己的,不包括继承关系的 构造函数
volatile constructor <T>[] declaredConstructors;
//包括继承关系的public的构造函数
volatile Constructor<T>[] publicConstructors;
// public and declared
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
//接口
volatile Class<?>[] interfaces;
包括后面提到的 getMethods 、 getDeclaredMethods 之类的,都是这个规则的区别。
先定义两个用来测试的类, Temp 及它的子类 TempChild 。这两个类里面,分别定义了三种修饰的方法和属性( public 、 protected 、 private )。
public class Temp {
public int public_Field_1;
private int private_Field_2;
protected int protected_Field_3;
public Temp(){
System.out.println("Temp Created");
}
public Void public_Foo_1(){
System.out.println("public_Foo_1");
}
private void private_Foo_2(){
System.out.println("private_Foo_2");
}
protected void protected_Foo_3(){
System.out.println("protected_Foo_3");
}
}
public class TempChild extends Temp {
public int public_Child_Field_1;
private int private_Child_Field_2;
protected int protected_Child_Field_3;
public void public_Child_Foo_1(){
System.out.println("public_Child_Foo_1");
}
private void private_Child_Foo_2(){
System.out.println("private_Child_Foo_2");
}
protected void protected_Child_Foo_3(){
System.out.println("protected_Child_Foo_3");
}
}
一个对象,最基本的就是构造、方法、属性,所以反射步骤,我们就按照这个来进行,逐步分析一下整个流程。
下面我们写代码,来反射 TempChild 这个类。
/**
*反射需要完整的包名
*通过包名获取要反射的对象
*/
Class<?> obj = Class.forName("com.myweb.demo.TempChild");
/**
*实例化这个对象
*/
Object temp = obj.newInstance();
反射,最终也要将对象实例化, newInstance 就是负责这个工作的,具体看看 newInstance 的实现。 需要注意的是newInstance是无参数的,如果你自己定义了一个带参数的构造函数,就会报错 。如果想使用带参数的构造,可以通过 Constructor 对象。
public T newInstance()
throws InstantiationException, IllegalAccessException
{
//先检查是否有设置权限,比如读写文件
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection .getCallerClass(), false);
}
//检查构造函数是否存在
if (cachedConstructor == null) {
//如果还没找到构造函数,再判断要反射的类是不是Class类,如果是,抛出异常,不能反射Class类
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
//获取所有访问级别的构造方法,并返回一个匹配的public的构造对象
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
//对于构造函数,允许获取更大的权限
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
//设置标记,不再检查权限
c.setAccessible(true);
return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();
//判断是不是public的
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
//如果不是,就要判断调用者是否是当前的进程使用者,也就是判断是否是第三方调用
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
//创建对象
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
创建对象时,先判断是否设置了系统权限,这个可以通过文件配制好的,比如对本地的某些文件,是否允许读写,网络是否可以访问等。可参考 Java 安全模型介绍( ),官方文档( ),后面会看到多处 AccessController.doPrivileged 的调用,都可以参考这两个文档。
如果设置了权限,那就判断要访问的对象是否符合你的权限设置。来看 checkMemberAccess 方法。
//判断是否允许访问成员,如果被拒绝,抛出SecurityException异常
private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {
final SecurityManager s = System.getSecurityManager();
if (s != null) {
/* 默认的可以访问PUBLIC成员,
* 也可以访问具有相同Loader和Caller的类
* 其他的,需要设置RuntimePermission("accessDeclaredMembers")权限
*/
final ClassLoader ccl = ClassLoader.getClassLoader(caller);
final ClassLoader cl = getClassLoader0();
if (which != Member.PUBLIC) {
if (ccl != cl) {
//既不是PUBLIC的,也不是来自同一个Loader和Caller的,那就通过SecurityManager去检查权限
s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
}
this.checkPackageAccess(ccl, checkProxyInterfaces);
}
}
系统默认是可以访问所有Public的成员的,还可以访问具有相同加载器和调用者的类,其他的都要向系统申请 RuntimePermission(“accessDeclaredMembers”) 权限。这两项如果都不符合,就会通过 SecurityManager 的 checkPermission 去检查是否配制了相关的权限。
回到newInstance进行看,如果权限符合之后,就是获取构造函数。
获取构造函数,需要获取包括父类、子类的构造,所以使用的是 DECLARED 。
//empty就是构造函数的参数,newInstance是不带参数的,所以是一个空数组
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int which) throws NoSuchMethodException
{
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
privateGetDeclaredConstructors 获取到 PUBLIC 的构造方法的数组,然后遍历这个数组,判断传入的参数集合与反射到的构造集合中的参数集合是否一致,如果一致,我们才能使用。
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
//检查系统的一些属性是否准备好,可以访问了
checkInitted();
Constructor<T>[] res;
Class.ReflectionData<T> rd = reflectionData();
//判断是否存在已经反射的结果
if (rd != null) {
//如果有,根据是否PUBLIC获取对应的构造函数的集合,如果存在,返回
res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
if (res != null) return res;
}
// 检查你反射的是不是接口
if (isInterface()) {
//如果是接口,创建一个空数组
@SuppressWarnings("unchecked")
Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
res = temporaryRes;
} else {
//如果不是接口,就获取构造函数集合
res = getDeclaredConstructors0(publicOnly);
}
//将数据保存
if (rd != null) {
if (publicOnly) {
rd.publicConstructors = res;
} else {
rd.declaredConstructors = res;
}
}
return res;
}
checkInitted 用来检查系统是否准备好,属性是否可以立即访问,比如想通过 System.out 去打印日志,就要判断 System.out 是否可用,如果没准备好,就给予最高权限去修改。
private static void checkInitted() {
if (initted) return;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
//如果System.out不可用
if (System.out == null) {
return null;
}
// Doesn't use Boolean.getBoolean to avoid class init.
String val = System.getProperty("sun.reflect.noCaches");
/**
*noCaches是否存在,来决定是否用缓存中的反射数据
*见private ReflectionData<T> reflectionData()
*/
if (val != null && val.equals("true")) {
useCaches = false;
}
initted = true;
return null;
}
});
}
checkInitted 通过之后,先获取 reflectionData ,其中的属性,在文字开头已经说明。然后判断 reflectionData 是否存在,如果存在就根据是否 PUBLIC ,来返回对应的构造函数的集合,如果集合不为空,返回,否则,继续执行后面代码。
判断你反射的是否是接口( Interface ),接口没有构造函数,所以会创建一个空数组返回,如果反射的不是接口,就通过 native 的方法 getDeclaredConstructors0 去获取。经过这些操作之后,把获得的构造函数的结合保存到 reflectionData 对象中,以便下次直接从内存中查找。
结果拿到之后,我们要从中找到一个匹配的构造方法,来创建实例。 arrayContentsEq 方法,通过比较你传入的参数集合(对应 newInstance 是空集合)和每一个 Constructor 对象中的参数集合,如果一致就会调用 Constructor 的 copy 方法,返回一个 Constructor 对象。
继续返回 newInstance 中,上面 getConstructor0 结果返回之后,结果会保存到 cachedConstructor 变量,随后我们又看到了 AccessController.doPrivileged ,告诉系统,我可以访问其中的任何成员。接下来又开始检查,执行 Reflection.quickCheckMemberAccess 。
public static boolean quickCheckMemberAccess(Class<?> var0, int var1) {
return Modifier.isPublic(getClassAccessFlags(var0) & var1);
}
比较直接,就是检查是否包含 PUBLC ,如果不包含,就获取当前调用者(Caller),并和 newInstanceCallerCache 比对,如果不一样,就通过 Reflection.ensureMemberAccess 来验证,调用者是否能够访问成员,不能就抛出异常,能就把当前的 Caller 赋值给 newInstanceCallerCache 。
最后通过 Constructor 的 newInstance 来创建实例。
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
//判断是否可以访问,否则就一通检查
if (! override ) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
//如果是枚举类型,抛出异常
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
override 变量就是前面Class的 newInstance 中 AccessController.doPrivileged 设置的。
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
public void setAccessible(boolean flag) throws SecurityException {
......
setAccessible0(this, flag);
}
private static void setAccessible0(AccessibleObject obj, boolean flag)
throws SecurityException
{
......
obj.override = flag;
}
随后,判断对象是否是枚举类型,如果是,抛出异常。
类型判断通过,就要构造一个构造函数访问器 ConstructorAccessor 。通过 ReflectionFactory 的 newConstructorAccessor 方法创建。并通过访问器的 newInstance(NativeConstructorAccessorImpl类中) 实例化对象。来简单看一下相关代码。
public ConstructorAccessor newConstructorAccessor(Constructor<?> var1) {
//检查系统属性是否准备好
checkInitted();
Class var2 = var1.getDeclaringClass();
//判断是否抽象类,是否Class类,是否匿名类等等
if (Modifier.isAbstract(var2.getModifiers())) {
return new InstantiationExceptionConstructorAccessorImpl((String)null);
} else if (var2 == Class.class) {
return new InstantiationExceptionConstructorAccessorImpl("Can not instantiate java.lang.Class");
} else if (Reflection.isSubclassOf(var2, ConstructorAccessorImpl.class)) {
return new BootstrapConstructorAccessorImpl(var1);
} else if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
return (new MethodAccessorGenerator()).generateConstructor(var1.getDeclaringClass(), var1.getParameterTypes(), var1.getExceptionTypes(), var1.getModifiers());
} else {
//创建NativeConstructorAccessorImpl对象,用于实例化对象
NativeConstructorAccessorImpl var3 = new NativeConstructorAccessorImpl(var1);
//初始化代理类,设置delegate变量为var3
DelegatingConstructorAccessorImpl var4 = new DelegatingConstructorAccessorImpl(var3);
var3.setParent(var4);
return var4;
}
}
这样,整个对象实例化的过程就结束了。
我们的对象创建完后,我们就可以使用了。我们可以把反射创建的实例转化为具体的对象,访问其中的方法和属性。也可以继续通过反射执行里面的方法和读写属性。