CallerSensitive ensures correct caller of a method is returned whenever method is invoked by reflection api or direct call.
All source code is available at github.
GetCallerClass in java 8
In this section we'll discuss how to get caller class in java 8
- Reflection.getCallerClass()
We can use this api: 1) in a method annotated with @CallerSensitive and 2) this method is in a class loaded by ExtClassLoader or BootstrapClassLoader
- Deprecated Reflection.getCallerClass(depth)
- Iterate stack trace from throwable instance
Sample source code
public class Main {
/**
* Reflection.getCallerClass() api without @CallerSensitive
* When Class is loaded by App/Ext/Bootstrap ClassLoader then java.lang.InternalError: CallerSensitive annotation expected at frame 1
*/
private static void getCallerClassWithoutCallerSensitive() {
try {
System.out.format("Method is called by %s%n", Reflection.getCallerClass());
} catch (Throwable e) {
System.out.println(e.getMessage());
}
}
/**
* Reflection.getCallerClass() api with @CallerSensitive
* When Class is loaded by AppClassLoader then java.lang.InternalError: CallerSensitive annotation expected at frame 1
* When Class is loaded by ExtClassLoader/BootstrapClassLoader, then works fine.
*/
@CallerSensitive
private static void getCallerClassWithCallerSensitive() {
try {
System.out.format("Method is called by %s%n", Reflection.getCallerClass());
} catch (Throwable e) {
System.out.println(e.getMessage());
}
}
/**
* Deprecated Reflection.getCallerClass(n) api
*/
private static void getCallerClassN() {
int i = 0;
while (Reflection.getCallerClass(i) != null) {
System.out.println(Reflection.getCallerClass(i++));
}
}
/**
* Iterate Throwable getStackTrace
*/
private static void getCallerClassByStackTrace() {
StackTraceElement[] stackTraces = new Throwable().getStackTrace();
Arrays.stream(stackTraces).forEach(e -> System.out.println(e.getClassName() + " " + e.getMethodName()));
}
private static void invoke(String methodName) throws Exception {
//direct call
System.out.format("Enter direct call %s%n", methodName);
switch (methodName) {
case "getCallerClassWithoutCallerSensitive":
getCallerClassWithoutCallerSensitive();
break;
case "getCallerClassWithCallerSensitive":
getCallerClassWithCallerSensitive();
break;
case "getCallerClassN":
getCallerClassN();
break;
case "getCallerClassByStackTrace":
getCallerClassByStackTrace();
break;
default:
break;
}
System.out.format("Exit direct call %s%n%n", methodName);
//reflection call
System.out.format("Enter reflection call %s%n", methodName);
Method method = Main.class.getDeclaredMethod(methodName);
method.invoke(null);
System.out.format("Exit reflection call %s%n%n", methodName);
}
public static void main(String[] args) throws Exception {
System.out.format("Main class loaded by %s%n%n", Main.class.getClassLoader());
invoke("getCallerClassWithoutCallerSensitive");
invoke("getCallerClassWithCallerSensitive");
invoke("getCallerClassN");
invoke("getCallerClassByStackTrace");
}
}
Compile and pack class files
mkdir -p out/production/caller-sensitive
export JAVA_HOME=`/usr/libexec/java_home -v 1.8`;
javac -d out/production/caller-sensitive $(find caller-sensitive/ -name '*.java')
cd out/artifacts/
jar cvf caller-sensitive.jar -C ../production/caller-sensitive/ .
Run sample code
Run in class path
java -cp caller-sensitive.jar com.cs.Main
Run in extension dir
java -Djava.ext.dirs=. com.cs.Main
Run in bootstrap class path
java -Xbootclasspath/a:caller-sensitive.jar com.cs.Main
Result
java8 getCallerClass.pngStackWalker in java 9
Get an instance of StackWalker
- StackWalker.Option
- RETAIN_CLASS_REFERENCE
- SHOW_REFLECT_FRAMES
- SHOW_HIDDEN_FRAMES
- StackWalker.StackFrame
An instance of StackFrame represents a method invocation.
- StackWalker.getInstance()
- StackWalker.getInstance(StackWalker.Option option)
- StackWalker.getInstance(Set<StackWalker.Option> options)
StackWalker APIs
- StackWalker.getCallerClass()
- StackWalker.forEach(Consumer<? super StackFrame> action)
- StackWalker.walk(Function<? super Stream<StackFrame>, ? extends T> function)
Sample source code
public class Main {
private static StackWalker RETAIN_CLASS_SW = StackWalker.getInstance(Set.of(Option.RETAIN_CLASS_REFERENCE));
private static StackWalker SHOW_REFLECT_SW = StackWalker.getInstance(Set.of(Option.RETAIN_CLASS_REFERENCE, Option.SHOW_REFLECT_FRAMES));
private static StackWalker SHOW_HIDDEN_SW = StackWalker.getInstance(Set.of(Option.RETAIN_CLASS_REFERENCE, Option.SHOW_HIDDEN_FRAMES));
private static Class getCallerClass() {
return RETAIN_CLASS_SW.getCallerClass();
}
private static Object printStackFrame() {
//print stack frame
forEachByRetainClass();
//print reflect stack frame
forEachByShowReflect();
//print hidden stack frame
forEachByShowHidden();
return null;
}
private static void forEachByRetainClass() {
RETAIN_CLASS_SW.forEach(System.out::println);
}
private static void forEachByShowReflect() {
SHOW_REFLECT_SW.forEach(System.out::println);
}
private static void forEachByShowHidden() {
SHOW_HIDDEN_SW.forEach(System.out::println);
}
private static void walkStackFrames() {
//count stack frame
Function<Stream<StackFrame>, Long> countStackFrameFun = stackFrameStream -> stackFrameStream.count();
Long stackFrameCount = SHOW_HIDDEN_SW.walk(countStackFrameFun);
System.out.println(stackFrameCount);
//StackWalker.forEach
Function<Stream<StackFrame>, Object> printStackFrameFun = stackFrameStream -> {
stackFrameStream.forEach(System.out::println);
return null;
};
SHOW_HIDDEN_SW.walk(printStackFrameFun);
//Dump stack frame to a list
Function<Stream<StackFrame>, List<StackFrame>> listStackFrameFun = stackFrameStream -> stackFrameStream.collect(Collectors.toList());
List<StackFrame> list = SHOW_HIDDEN_SW.walk(listStackFrameFun);
list.forEach(System.out::println);
}
private static void assertion(Class callerClass) {
assert callerClass == Main.class : "Expected Main.class, got " + callerClass;
}
/**
* VM options -enableassertions
*
* @param args
*/
public static void main(String[] args) throws Exception {
//direct call getCallerClass
Class callerClass = getCallerClass();
assertion(callerClass);
//lambda call getCallerClass
Supplier<Class> getCallerClass = Main::getCallerClass;
callerClass = getCallerClass.get();
assertion(callerClass);
//reflection call getCallerClass
callerClass = (Class) Main.class.getDeclaredMethod("getCallerClass").invoke(Main.class);
assertion(callerClass);
//direct call printStackFrame
printStackFrame();
//lambda call printStackFrame
Supplier<Object> printStackFrame = Main::printStackFrame;
printStackFrame.get();
//reflection call printStackFrame
Main.class.getDeclaredMethod("printStackFrame").invoke(Main.class);
walkStackFrames();
}
}
Result
java 9 stack walk api.pngConclusion
- In java 8, Reflection.getCallerClass works with @CallerSensitive and ExtClassLoader/BootstrapClassLoader
- Since java 9, we can use StackWalker.getCallerClass() to get caller class.
Reference
JEP 176: Mechanical Checking of Caller-Sensitive Methods
JDK-8020968 : Provide a replacement for sun.reflect.Reflection.getCallerClass
JDK-8043814 : JEP 259: Stack-Walking API
网友评论