sun.reflect.Reflection介绍以及@CallerSensitive注解

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

猜你喜欢

转载自blog.csdn.net/liuxiao723846/article/details/110008081