Unsafe类的学习之Reflection.getCallerClass()方法(二)

1,584 阅读2分钟

打开Unsafe类的源码,查看其获取unsafe实例方法,如下图


可以看到一个Reflection.getCallerClass() 的方法。点进去查看
注释机翻:返回调用此方法的方法的调用者的类,忽略与java.lang.reflect.Method.invoke()相关联的框架及其实现

如果不深究还琢磨不太明白其含义。我们看到在该方法上,有一个注解 @CallerSensitive,这又是干嘛用的呢?
通过百度,查找到JVM注解@CallSensitive的解答:为了解决通过反射导致的安全漏洞。点击文章查阅安全漏洞详情。

A->反射1->反射2->反射3...->N->Reflection.getCallerClass(),最终可以获取到A的Class,忽略了中间的反射过程。而没有该注解,且调用了另外加深度的方法如:Reflection.getCallerClass(2),将获取不到A。加深度的方法在jdk9将移除,作为虚拟机内部方法。

所以Reflection.getCallerClass()方法就是获取到调用该方法的源头类的Class。 举个例子更能说明,当我们查看报错日志,如NullPointException,我们可以看到具体在哪个类多少行调用出错的,就是使用了该方法配合反射得到。

不知道大家是否注意到,在getUnsafe()方法上,也有注解 @CallerSensitive,这是为什么呢?
Java进阶:@CallerSensitive详解对此做出了解答:

Reflection.getCallerClass()要求调用者必须有@CallerSensitive注解,并且必须有权限(由bootstrap class loader或者extension class loader加载的类)才可以调用。

最后拓展一下关于Reflection.getCallerClass()加入深度参数的例子,参考Reflection的getCallerClass的使用 ,这里主要对该文章进行详解 。

样例如下:

package lee;
import sun.reflect.Reflection;

public class Test {
    public static void main(String[] args) {
        Test2 test=new Test2();
        test.g();
    }
}
 
public class Test2 {
    public  void g(){
        gg();
    }
    public  void gg(){
        System.out.println("-1 : "+Reflection.getCallerClass(-1));
        System.out.println("0 : "+Reflection.getCallerClass(0));
        System.out.println("1 : "+Reflection.getCallerClass(1));
        System.out.println("2 : "+Reflection.getCallerClass(2));
        System.out.println("3 : "+Reflection.getCallerClass(3));
        System.out.println("4 : "+Reflection.getCallerClass(4));
        System.out.println("5 : "+Reflection.getCallerClass(5));
    }
}

输出结果:

-1 : class sun.reflect.Reflection
0 : class sun.reflect.Reflection
1 : class lee.Test2
2 : class lee.Test2
3 : class lee.Test
4 : null
5 : null

这里捋一下调用路径:Test -> Test2 -> g() -> gg()
1:意味着对gg()而言的上一级,即g(),g()所属Tast2类,故返回为Test2
2:对gg()而言的上二级,即Test2,所以为Test2
3:以此类推上三级,即Test 4、5因为没有类再调用Test,所以为null

总结深度为0或小于0时,返回为Reflection类本身,否则为该深度的调用类