Class
对象提供以下获取对象的方法( Method
):
getMethod getDeclaredMethod getMethods getDeclaredMethods
测试用例
//父类 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(); } } 复制代码
先看 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);
其实这个注解是Java修复漏洞用的。防止使用者使用双重反射来提升权限,原理是应为当时反射只检查深度的调用者的类是否有权限,本来我本身的类是没有这么高权限的,但是我可以通过多重反射来提高调用的权限。
举个栗子,比如在反射类中某个方法需要向上找固定两层的调用者是否有权限(反射相关的类权限是比较高的),我可以通过 我自己的类 -> 反射1 ->反射2这样的调用链上,反射2检查的是反射1的权限,导致安全漏洞。使用这样的注解,那么 getCallerClass
就会直接跳过有 @CallerSensitive
修饰的接口方法,直接查找真实的调用者( actual caller
)。
先看这个方法的源码:
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()方法的返回值,所以也称为系统类加载器.一般情况下这就是系统默认的类加载器.
继续看源码:
/** @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。
继续源码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)
的方法。
看看源码:
@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; } 复制代码
源码如下:
@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的获取方法源码分析都已梳理完毕,~(@^_^@)~喜欢的话还请顶一个赞,您的支持是我更新的最大动力。
由于笔者技术有限,文章难免有些许错误,欢迎━( `∀´ )ノ指正!