上述@CallerSensitive
注释方法暗示了什么?
例如,注释出现在Class
的getClassLoader方法中 @CallerSensitive
public ClassLoader getClassLoader() {
//
}
根据我在评论中链接的JEP(也在这里),
对调用者敏感的方法根据类的不同而改变其行为它的直接呼叫者。它通过调用来发现调用者的类sun.reflect.Reflection.getCallerClass
法
如果你看Class#forName(String)
的实现
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
return forName0(className, true,
ClassLoader.getClassLoader(Reflection.getCallerClass()));
}
时,您注意到它正在使用Reflection.getCallerClass()
。如果我们看这个方法
返回调用此方法的方法调用者的类,忽略与
java.lang.reflect.Method.invoke()
及其实现相关的帧。
@CallerSensitive
public static native Class getCallerClass();
在这个JEP之前,问题似乎是,如果调用者敏感的方法是通过反射而不是直接调用的,那么必须有一个复杂的过程来识别实际调用类是什么。如果通过反射调用该方法,则会出现问题。一个更简单的过程被提出(并引入)@CallerSensitive
。
基本上,JVM使用@CallerSensitive
注释
JVM将跟踪该注释,并可选地强制执行不变,
sun.reflect.Reflection.getCallerClass
方法可以只报告一个方法的调用者,当该方法被标记为这个注释。
From jdk.internal.reflect.CallerSensitive
注释了@CallerSensitive的方法对它的调用类敏感,通过反射。
自Java SE 9以来,等效的是java.lang.StackWalker.getCallerClass
。
这实际上是Java 1.0和1.1版本的安全模型,它们实现了一种乞丐的链接器检查。除了与反射相关的内容外,这是一种连贯的方法,但它非常脆弱。另一方面,更严格的Java 2安全模型是神奇的,并且没有显示其工作原理。
@CallerSensitive
方法根据调用它们的类改变行为。这总是令人惊讶,但Java 2堆栈检查安全模型也是如此。更糟糕的是,这些方法调用对于代表其调用者工作的类特别有用,因此上下文无论如何都是错误的。
Java SE的安全编码指南涵盖了这个领域。
- 指南9-8/ACCESS-8:根据直接调用者的类加载器,安全地调用绕过SecurityManager检查的标准api 指南9-9/ACCESS-9:使用直接调用者的类加载器实例安全地调用执行任务的标准api
- 指南9-10/access -10:注意对直接调用者执行Java语言访问检查的标准api
- 指南9-11/ACCESS-11:注意java.lang.reflect.Method.invoke在检查直接调用者时被忽略 指南9-12/ACCESS-12:避免在接口类中使用调用者敏感的方法名
Java SE 9中模块的引入意味着一些细节发生了变化。
另外,如果Method.invoke
调用@CallerSensitive
方法,getCallerClass
会忽略一些堆栈帧。在内部,JDK使用一个"蹦床"ClassLoader
作为一个良性的虚拟调用者。java.lang.invoke.MethodHandle
复制Method.invoke
的问题。AccessController.doPrivileged
方法也有问题,规范的结果令人惊讶。
JEP 176:对调用者敏感方法的机械检查处理特定的@CallerSensitive
jdk内部注释。指南中的方法列表是由FindBugs插件生成的,但随后手动更新。