转载

Java反射原理分析 - Method篇

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();
     }
}
复制代码

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的获取方法源码分析都已梳理完毕,~(@^_^@)~喜欢的话还请顶一个赞,您的支持是我更新的最大动力。

由于笔者技术有限,文章难免有些许错误,欢迎━( `∀´ )ノ指正!

原文  https://juejin.im/post/5da6be7a6fb9a04dee182727
正文到此结束
Loading...