sun.reflect.Reflection 这个工具类是和反射相关的,我第一次见到这个方法是在java.sql.DriverManager中的getConnection方法中见到的:
@CallerSensitive
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
上面的代码是在Java 8下,在DriverManager中使用它的目的是为了获得相应的ClassLoader。Reflection.getCallerClass()是一个native方法,返回的是Class<?>类型:
//Reflection 类
@CallerSensitive
public static native Class<?> getCallerClass();
在Java 7下,为获得相应的ClassLoader,DriverManager就直接提供了native的方法:
/* Returns the caller's class loader, or null if none */
private static native ClassLoader getCallerClassLoader();
最近,在clazz.forName()的源码中有一次看见了Reflection.getCallerClass()的身影:(java8下)
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
我们发现在调用Reflection.getCallerClass()时都加了一个注解@CallerSensitive,那么今天就来看下这个注解是干啥的?
1、@CallerSensitive注解:
jdk内有些方法,jvm的开发者认为这些方法危险(一般是sun包下的类),不希望开发者调用,就把这种危险的方法用 @CallerSensitive修饰,并在“jvm”级别检查。(见:http://openjdk.java.net/jeps/176)
如Reflection.getCallerClass()方法规定,调用它的对象,必须有 @CallerSensitive 注解,否则 报异常 Exception in thread "main" java.lang.InternalError: CallerSensitive annotation expected at frame 1
@CallerSensitive还有个特殊之处,必须由 启动类classloader加载(如rt.jar ),才可以被识别。 所以rt.jar下面的注解可以正常使用。 开发者自己写的@CallerSensitive 不可以被识别。 但是,可以利用jvm参数 -Xbootclasspath/a: path 假装自己的程序是启动类。
2、-Xbootclasspath介绍:
-Xbootclasspath:bootclasspath :让jvm从指定的路径中加载bootclass,用来替换jdk的rt.jar。一般不会用到。
-Xbootclasspath/a: path : 被指定的文件追加到默认的bootstrap路径中。
-Xbootclasspath/p: path : 让jvm优先于默认的bootstrap去加载path中指定的class。
示例1:
public static void test0(String[] args) {
Class<?> caller = Reflection.getCallerClass();
System.out.println(caller.getClassLoader());
}
运行会报如下错误:
java.lang.InternalError: CallerSensitive annotation expected at frame 1
at sun.reflect.Reflection.getCallerClass(Native Method)
at cn.nuc.edu.LogTest.ClassLoaderTest.test0(ClassLoaderTest.java:40)
at cn.nuc.edu.LogTest.ClassLoaderTest.main(ClassLoaderTest.java:24)
需要在方法上添加 @CallerSensitive,其次,在启动时添加参数:-Xbootclasspath/a:E:\workspace_test\LogTest\target\classes
3、Reflection.getCallerClass()介绍:
上面说了,该方法是一个native的,用来返回调用者的Class对象,进而通过class对象可以获取调用者的类加载器。看一个例子:
package cn.nuc.edu.LogTest.reflection;
import sun.reflect.Reflection;
import sun.reflect.CallerSensitive;
public class CalleeApp {
@CallerSensitive
public void call() {
Class<?> clazz = Reflection.getCallerClass();
System.out.println("Hello " + clazz);
}
}
package cn.nuc.edu.LogTest.reflection;
public class CallerApp {
public static void main(String[] args) {
CalleeApp app = new CalleeApp();
Caller1 c1 = new Caller1();
c1.run(app);
}
static class Caller1 {
void run(CalleeApp calleeApp) {
if (calleeApp == null) {
throw new IllegalArgumentException("callee can not be null");
}
calleeApp.call();
}
}
}
运行CallerApp,并且添加JVM启动参数:-Xbootclasspath/a:E:\workspace_test\LogTest\target\classes
输出:Hello class cn.nuc.edu.LogTest.reflection.CallerApp$Caller1