在研究Unsafe类的时候发现了一段有趣的代码,如下:
public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2); // 获取调用栈信息
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
深入看了下,原来这段代码的目的是为了对Unsafe的安全使用做控制,不允许其他类直接调用Unsafe的静态方法getUnsafe():(直接调用会抛异常)
Class cc = sun.reflect.Reflection.getCallerClass(2);
这段代码是获取类的调用栈信息,参数i(int 类型)表示栈帧的层次,比如i=0返回Reflection类本身,i=1则返回调用Refelection的类Unsafe,i=2则返回调用Unsafe类的类,这样就可以判断Unsafe是否是直接被调用的。不过我们可以绕过这个限制,使用Java的反射调用完成:
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
这样就可以使用Unsafe了。下面是Refection类的源码:
/*
* %W% %E%
*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package sun.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/** Common utility routines used by both java.lang and
java.lang.reflect */
public class Reflection {
/** Used to filter out fields from certain classes from public
view, where those fields may contain VM-internal objects */
private static Map/*<Class, String[]>*/ fieldFilterMap = Collections.synchronizedMap(new HashMap());
/** Returns the class of the method <code>realFramesToSkip</code>
frames up the stack (zero-based), ignoring frames associated
with java.lang.reflect.Method.invoke() and its implementation.
The first frame is that associated with this method, so
<code>getCallerClass(0)</code> returns the Class object for
sun.reflect.Reflection. Frames associated with
java.lang.reflect.Method.invoke() and its implementation are
completely ignored and do not count toward the number of "real"
frames skipped. */
public static native Class getCallerClass(int realFramesToSkip);
/** Retrieves the access flags written to the class file. For
inner classes these flags may differ from those returned by
Class.getModifiers(), which searches the InnerClasses
attribute to find the source-level access flags. This is used
instead of Class.getModifiers() for run-time access checks due
to compatibility reasons; see 4471811. Only the values of the
low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
valid. */
private static native int getClassAccessFlags(Class c);
/** A quick "fast-path" check to try to avoid getCallerClass()
calls. */
public static boolean quickCheckMemberAccess(Class memberClass,
int modifiers)
{
return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers);
}
public static void ensureMemberAccess(Class currentClass,
Class memberClass,
Object target,
int modifiers)
throws IllegalAccessException
{
if (currentClass == null || memberClass == null) {
throw new InternalError();
}
if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) {
throw new IllegalAccessException("Class " + currentClass.getName() +
" can not access a member of class " +
memberClass.getName() +
" with modifiers \"" +
Modifier.toString(modifiers) +
"\"");
}
}
public static boolean verifyMemberAccess(Class currentClass,
// Declaring class of field
// or method
Class memberClass,
// May be NULL in case of statics
Object target,
int modifiers)
{
// Verify that currentClass can access a field, method, or
// constructor of memberClass, where that member's access bits are
// "modifiers".
boolean gotIsSameClassPackage = false;
boolean isSameClassPackage = false;
if (currentClass == memberClass) {
// Always succeeds
return true;
}
if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
isSameClassPackage = isSameClassPackage(currentClass, memberClass);
gotIsSameClassPackage = true;
if (!isSameClassPackage) {
return false;
}
}
// At this point we know that currentClass can access memberClass.
if (Modifier.isPublic(modifiers)) {
return true;
}
boolean successSoFar = false;
if (Modifier.isProtected(modifiers)) {
// See if currentClass is a subclass of memberClass
if (isSubclassOf(currentClass, memberClass)) {
successSoFar = true;
}
}
if (!successSoFar && !Modifier.isPrivate(modifiers)) {
if (!gotIsSameClassPackage) {
isSameClassPackage = isSameClassPackage(currentClass,
memberClass);
gotIsSameClassPackage = true;
}
if (isSameClassPackage) {
successSoFar = true;
}
}
if (!successSoFar) {
return false;
}
if (Modifier.isProtected(modifiers)) {
// Additional test for protected members: JLS 6.6.2
Class targetClass = (target == null ? memberClass : target.getClass());
if (targetClass != currentClass) {
if (!gotIsSameClassPackage) {
isSameClassPackage = isSameClassPackage(currentClass, memberClass);
gotIsSameClassPackage = true;
}
if (!isSameClassPackage) {
if (!isSubclassOf(targetClass, currentClass)) {
return false;
}
}
}
}
return true;
}
private static boolean isSameClassPackage(Class c1, Class c2) {
return isSameClassPackage(c1.getClassLoader(), c1.getName(),
c2.getClassLoader(), c2.getName());
}
/** Returns true if two classes are in the same package; classloader
and classname information is enough to determine a class's package */
private static boolean isSameClassPackage(ClassLoader loader1, String name1,
ClassLoader loader2, String name2)
{
if (loader1 != loader2) {
return false;
} else {
int lastDot1 = name1.lastIndexOf('.');
int lastDot2 = name2.lastIndexOf('.');
if ((lastDot1 == -1) || (lastDot2 == -1)) {
// One of the two doesn't have a package. Only return true
// if the other one also doesn't have a package.
return (lastDot1 == lastDot2);
} else {
int idx1 = 0;
int idx2 = 0;
// Skip over '['s
if (name1.charAt(idx1) == '[') {
do {
idx1++;
} while (name1.charAt(idx1) == '[');
if (name1.charAt(idx1) != 'L') {
// Something is terribly wrong. Shouldn't be here.
throw new InternalError("Illegal class name " + name1);
}
}
if (name2.charAt(idx2) == '[') {
do {
idx2++;
} while (name2.charAt(idx2) == '[');
if (name2.charAt(idx2) != 'L') {
// Something is terribly wrong. Shouldn't be here.
throw new InternalError("Illegal class name " + name2);
}
}
// Check that package part is identical
int length1 = lastDot1 - idx1;
int length2 = lastDot2 - idx2;
if (length1 != length2) {
return false;
}
return name1.regionMatches(false, idx1, name2, idx2, length1);
}
}
}
static boolean isSubclassOf(Class queryClass,
Class ofClass)
{
while (queryClass != null) {
if (queryClass == ofClass) {
return true;
}
queryClass = queryClass.getSuperclass();
}
return false;
}
// fieldNames must contain only interned Strings
public static void registerFieldsToFilter(Class containingClass,
String[] fieldNames) {
fieldFilterMap.put(containingClass, fieldNames);
}
public static Field[] filterFields(Class containingClass,
Field[] fields) {
if (fieldFilterMap == null) {
// Bootstrapping
return fields;
}
String[] filteredFieldNames = (String[]) fieldFilterMap.get(containingClass);
if (filteredFieldNames == null) {
return fields;
}
int numNewFields = 0;
for (int srcIdx = 0; srcIdx < fields.length; srcIdx++) {
boolean shouldSkip = false;
Field field = fields[srcIdx];
for (int tmp = 0; tmp < filteredFieldNames.length; tmp++) {
if (field.getName() == filteredFieldNames[tmp]) {
shouldSkip = true;
break;
}
}
if (!shouldSkip) {
++numNewFields;
}
}
Field[] newFields = new Field[numNewFields];
int destIdx = 0;
for (int srcIdx = 0; srcIdx < fields.length; srcIdx++) {
boolean shouldSkip = false;
Field field = fields[srcIdx];
for (int tmp = 0; tmp < filteredFieldNames.length; tmp++) {
if (field.getName() == filteredFieldNames[tmp]) {
shouldSkip = true;
break;
}
}
if (!shouldSkip) {
newFields[destIdx++] = field;
}
}
return newFields;
}
}