反射方法剖析
Class
对象提供以下获取对象的方法(Method
):
getMethod
- 返回类或接口的特定方法,只能是public修饰的方法。getDeclaredMethod
- 返回类或接口的特定方法。getMethods
- 返回类或接口的所有public方法,包括父类的public方法。getDeclaredMethods
- 返回类或接口的所有方法,包括 public、protected、默认(包)访问和 private 方法,但不包括继承的方法。
测试用例
//父类
public class RefFather {
public void refFatherMethod(){
System.out.println("我是父类方法 refFatherMethod");
}
}
//子类
public class RefSon extends RefFather{
private void refSonMethod(){
System.out.println("我是子类方法 refMethod");
}
}
//测试方法
public static void main(String[] args) {
try {
//类的全限定名 即包名+类名
Class<?> clz = Class.forName("main.ref.RefSon");
Object obj = clz.newInstance();
Method refFatherMethod = clz.getMethod("refFatherMethod");
Method refSonMethod = clz.getDeclaredMethod("refSonMethod");
refSonMethod.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
Method
先看Class.getMethod(String name, Class<?>... parameterTypes)
的源码:
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
复制代码
上面源码我们只需要重点解决以下疑问就可以知道
getMethod
方法究竟是怎么实现的。
@CallerSensitive
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Reflection.getCallerClass()
Method method = getMethod0(name, parameterTypes, true);
@CallerSensitive
其实这个注解是Java修复漏洞用的。防止使用者使用双重反射来提升权限,原理是应为当时反射只检查深度的调用者的类是否有权限,本来我本身的类是没有这么高权限的,但是我可以通过多重反射来提高调用的权限。
举个栗子,比如在反射类中某个方法需要向上找固定两层的调用者是否有权限(反射相关的类权限是比较高的),我可以通过 我自己的类 -> 反射1 ->反射2这样的调用链上,反射2检查的是反射1的权限,导致安全漏洞。使用这样的注解,那么 getCallerClass
就会直接跳过有 @CallerSensitive
修饰的接口方法,直接查找真实的调用者(actual caller
)。
checkMemberAccess
先看这个方法的源码:
private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {
final SecurityManager s = System.getSecurityManager();
if (s != null) {
final ClassLoader ccl = ClassLoader.getClassLoader(caller);
final ClassLoader cl = getClassLoader0();
if (which != Member.PUBLIC) {
if (ccl != cl) {
s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
}
this.checkPackageAccess(ccl, checkProxyInterfaces);
}
}
复制代码
他的功能主要是检查是否允许客户端访问成员。
默认策略:允许所有客户端使用普通Java访问权限进行访问控制。
什么意思呢?
就是如果你没有主动配置 SecurityManager
安全管理器,则按照Class类的访问修饰符来判断是否有权限。例如 protected
只允许同包,或者子类访问,跨包则变为 private 禁止访问。
final ClassLoader ccl = ClassLoader.getClassLoader(caller);
final ClassLoader cl = getClassLoader0();
如果有自定义的 SecurityManager
(调用 System.setSecurityManager(new MySecurityManager())
),就会去判断调用方与访问方是否具有相同的类加载器,如果有,即便不是public访问修饰符,也没有权限的限制。一般情况下这里的类加载器 ClassLoader
都是 应用程序类加载器 Application ClassLoader
。
这里简单介绍一下Application ClassLoader
:
这个类加载器负责加载用户类路径(CLASSPATH)下的类库,一般我们编写的java类都是由这个类加载器加载,这个类加载器是CLassLoader中的getSystemClassLoader()方法的返回值,所以也称为系统类加载器.一般情况下这就是系统默认的类加载器.
Reflection.getCallerClass()
继续看源码:
/** @deprecated */
@Deprecated
public static native Class<?> getCallerClass(int var0);
复制代码
native 代表这个方法其实是由其他语言实现的,所以这里只说明用法。
简单介绍一下
Reflection.getCallerClass(int var0)
,有兴趣的可以了解一下。如果var0 的值 等于0或者小于0,则返回
class sun.reflect.Reflection
;如果var0 的值 等于1,则返回返回自己的类。 即是这行代码在哪个类里面,就返回这个类。
如果var0 的值 等于2,则返回返回调用者的类。举个栗子,这个方法在A的methodA中,在方法B中调用A的methodA,这时候
Reflection.getCallerClass(int var0)
返回的就是 class B。
getMethod0(name, parameterTypes, true)
继续源码OvO:
private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
MethodArray interfaceCandidates = new MethodArray(2);
Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
if (res != null)
return res;
// Not found on class or superclass directly
interfaceCandidates.removeLessSpecifics();
return interfaceCandidates.getFirst(); // may be null
}
private Method privateGetMethodRecursive(String name,
Class<?>[] parameterTypes,
boolean includeStaticMethods,
MethodArray allInterfaceCandidates) {
Method res;
// Search declared public methods
if ((res = searchMethods(privateGetDeclaredMethods(true),
name,
parameterTypes)) != null) {
if (includeStaticMethods || !Modifier.isStatic(res.getModifiers()))
return res;
}
// Search superclass's methods
if (!isInterface()) {
Class<? super T> c = getSuperclass();
if (c != null) {
if ((res = c.getMethod0(name, parameterTypes, true)) != null) {
return res;
}
}
}
// Search superinterfaces' methods
Class<?>[] interfaces = getInterfaces();
for (Class<?> c : interfaces)
if ((res = c.getMethod0(name, parameterTypes, false)) != null)
allInterfaceCandidates.add(res);
// Not found
return null;
}
private static Method searchMethods(Method[] methods,
String name,
Class<?>[] parameterTypes)
{
Method res = null;
String internedName = name.intern();
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;
}
return (res == null ? res : getReflectionFactory().copyMethod(res));
}
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData();
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;
}
复制代码
当调用
getMethod0
时,他调用了Class 私有的方法privateGetMethodRecursive
递归获得这个类的所有public
修饰的方法(包含父类)。在
privateGetMethodRecursive
中 会先对当前类所有方法进行匹配,如果不存在则会查找他的父类,如果父类也没有则会继续递归,直到顶级父类Object
。如果存在则返回这个Method的副本。
privateGetDeclaredMethods(boolean publicOnly)
参数为true,则返回这个类的所有 public方法,否则返回所有修饰符的方法。
根据privateGetDeclaredMethods
,我们不难理解为什么getMethods()返回的是包含父类的所有public方法,而getDeclaredFields()
获得是当前方法的所有修饰符方法。
大胆推测一下,getMethods()
应该是递归调用 privateGetDeclaredMethods(true)
的方法,而getDeclaredMethods()
应该只调用了一次 privateGetDeclaredMethods(false)
的方法。
getMethods
看看源码:
@CallerSensitive
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}
private Method[] privateGetPublicMethods() {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = rd.publicMethods;
if (res != null) return res;
}
//没有可用的缓存值;递归计算值。
//从获取公共声明的方法开始
MethodArray methods = new MethodArray();
{
//请注意这里
Method[] tmp = privateGetDeclaredMethods(true);
methods.addAll(tmp);
}
...
return res;
}
复制代码
getDeclaredMethods
源码如下:
@CallerSensitive
public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
//请注意这里
return copyMethods(privateGetDeclaredMethods(false));
}
复制代码
果然都和预想中的一样。
最后还有一个getDeclaredMethod
方法没有介绍,但相信大家看了以上的源码一定也能猜到实现的过程了。
@CallerSensitive
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;
}
复制代码
以上所有关于Class的获取方法源码分析都已梳理完毕,~(@^_^@)~喜欢的话还请顶一个赞,您的支持是我更新的最大动力。
由于笔者技术有限,文章难免有些许错误,欢迎━(`∀´)ノ指正!