深入理解Java中的反射機(jī)制及使用原理!詳細(xì)解析invoke方法的執(zhí)行和使用
反射的概念
-
反射: Refelection,反射是Java的特征之一,允許運(yùn)行中的Java程序獲取自身信息,并可以操作類或者對(duì)象的內(nèi)部屬性
- 通過(guò)反射,可以在運(yùn)行時(shí)獲得程序或者程序中的每一個(gè)類型的成員活成成員的信息
- 程序中的對(duì)象一般都是在編譯時(shí)就確定下來(lái),Java反射機(jī)制可以動(dòng)態(tài)地創(chuàng)建對(duì)象并且調(diào)用相關(guān)屬性,這些對(duì)象的類型在編譯時(shí)是未知的
- 也就是說(shuō) ,可以通過(guò)反射機(jī)制直接創(chuàng)建對(duì)象,即使這個(gè)對(duì)象類型在編譯時(shí)是未知的
-
Java反射提供下列功能:
- 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類
- 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
- 在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法,可以通過(guò)反射調(diào)用private方法
- 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法
反射的原理
- 反射的核心: JVM在運(yùn)行時(shí)才動(dòng)態(tài)加載類或者調(diào)用方法以及訪問(wèn)屬性,不需要事先(比如編譯時(shí))知道運(yùn)行對(duì)象是什么
-
類的加載:
- Java反射機(jī)制是圍繞Class類展開(kāi)的
-
首先要了解類的加載機(jī)制:
- JVM使用ClassLoader將字節(jié)碼文件,即 class文件加載到方法區(qū)內(nèi)存中
Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.mypackage.MyClass");
ClassLoader類根據(jù)類的完全限定名加載類并返回一個(gè)Class對(duì)象
-
ReflectionData:
- 為了提高反射的性能,必須要提供緩存
- class類內(nèi)部使用一個(gè)useCaches靜態(tài)變量來(lái)標(biāo)記是否使用緩存
- 這個(gè)值可以通過(guò)外部的sun.reflect.noCaches配置是否禁用緩存
- class類內(nèi)部提供了一個(gè)ReflectionData內(nèi)部類用來(lái)存放反射數(shù)據(jù)的緩存,并聲明了一個(gè)reflectionData域
- 由于稍后進(jìn)行按需延遲加載并緩存,所以這個(gè)域并沒(méi)有指向一個(gè)實(shí)例化的ReflectionData對(duì)象
// 標(biāo)記是否使用緩存,可以通過(guò)外部的sun.reflect.noCaches配置是否禁用緩存 private static boolean useCaches = true; static class ReflectionData<T> { volatile Field[] declaredFields; volatile Field[] publicFields; volatile Method[] declaredMethods; volatile Method[] publicMethods; volatile Constructor<T>[] declaredConstructors; volatile Constructors<T>[] publicConstructors; volatile Field[] declaredPublicFields; volatile Method[] declaredPublicMethods; final int redefinedCount;
ReflectionData(int redefinedCount) { this.redefinedCount = redefinedCount;
}
} // 這個(gè)是SoftReference,在內(nèi)存資源緊張的時(shí)候可能會(huì)被回收 // volatile保證多線程環(huán)境下讀寫的正確性 private volatile transient SoftReference<RefelectionData<T>> reflectionData; // 這個(gè)主要用于和ReflectionData中的redefinedCount進(jìn)行比較 // 如果兩個(gè)值不相等,說(shuō)明ReflectionData緩存的數(shù)據(jù)已經(jīng)過(guò)期了 private volatile transient classRedefinedCount = 0;
反射的主要用途
-
反射最重要的用途就是開(kāi)發(fā)各種通用框架
- 很多框架都是配置化的,通過(guò)XML文件配置Bean
- 為了保證框架的通用性,需要根據(jù)配置文件加載不同的對(duì)象或者類,調(diào)用不同的方法
- 要運(yùn)用反射,運(yùn)行時(shí)動(dòng)態(tài)加載需要加載的對(duì)象
-
示例:
- 在運(yùn)用Struts 2框架的開(kāi)發(fā)中會(huì)在struts.xml中配置Action:
<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute"> <result>/shop/shop-index.jsp</result> <result name="error">login.jsp</result> </action>
- 配置文件與Action建立映射關(guān)系
- 當(dāng)View層發(fā)出請(qǐng)求時(shí),請(qǐng)求會(huì)被StrutsPrepareAndExecuteFilter攔截
-
StrutsPrepareAndExecuteFilter會(huì)動(dòng)態(tài)地創(chuàng)建Action實(shí)例
- 請(qǐng)求login.action
- StrutsPrepareAndExecuteFilter會(huì)解析struts.xml文件
- 檢索action中name為login的Action
- 根據(jù)class屬性創(chuàng)建SimpleLoginAction實(shí)例
- 使用invoke方法調(diào)用execute方法
- 反射是各種容器實(shí)現(xiàn)的核心
反射的運(yùn)用
- 反射相關(guān)的類在StrutsPrepareAndExecuteFilter包
-
反射可以用于:
- 判斷對(duì)象所屬的類
- 獲得class對(duì)象
- 構(gòu)造任意一個(gè)對(duì)象
- 調(diào)用一個(gè)對(duì)象
-
九大預(yù)定義的Class對(duì)象:
- 基本的Java類型: boolean, byte, char, short, int, long, float, double
- 關(guān)鍵字void通過(guò)class屬性的也表示為Class對(duì)象
-
包裝類或者void類的靜態(tài)TYPE字段:
- Integer.TYPE == int.class
- Integer.class == int.class
-
數(shù)組類型的Class實(shí)例對(duì)象:
- Class<String[]> clz = String[].class;
-
數(shù)組的Class對(duì)象如何比較是否相等:
- 數(shù)組的維數(shù)
- 數(shù)組的類型
- Class類中的isArray(),用來(lái)判斷是否表示一個(gè)數(shù)組類型
獲得Class對(duì)象
- 使用Class類的forName靜態(tài)方法:
public static Class<?> forName(String className); /* 在JDBC中使用這個(gè)方法加載數(shù)據(jù)庫(kù)驅(qū)動(dòng) */ Class.forName(driver);
- 直接獲取一個(gè)對(duì)象的class:
Class<?> klass=int.class;
Class<?> classInt=Integer.TYPE;
- 調(diào)用對(duì)象的getClass()方法:
StringBuilder str=new StringBuilder("A");
Class<?> klass=str.getClass();
判斷是否是某個(gè)類的實(shí)例
- 一般來(lái)說(shuō),使用instanceof關(guān)鍵字判斷是否為某個(gè)類的實(shí)例
- 在反射中,可以使用Class對(duì)象的isInstance() 方法來(lái)判斷是否為某個(gè)類的實(shí)例,這是一個(gè)native方法
public native boolean isInstance(Object obj);
創(chuàng)建實(shí)例
通過(guò)反射生成對(duì)象的實(shí)例主要有兩種方式:
- 使用Class對(duì)象的newInstance()方法來(lái)創(chuàng)建Class對(duì)象對(duì)應(yīng)類的實(shí)例:
Class<?> c = String.class;
Object str = c.newInstance();
- 先通過(guò)Class對(duì)象獲取指定的Constructor對(duì)象,再調(diào)用Constructor對(duì)象的newInstance()方法來(lái)創(chuàng)建實(shí)例: 可以用指定的構(gòu)造器構(gòu)造類的實(shí)例
/* 獲取String所對(duì)應(yīng)的Class對(duì)象 */ Class<?> c=String.class; /* 獲取String類帶一個(gè)String參數(shù)的構(gòu)造器 */ Constructor constructor=c.getConstructor(String.class); /* 根據(jù)構(gòu)造器創(chuàng)建實(shí)例 */ Object obj=constructor.newInstance("abc");
System.out.println(obj);
獲取方法
獲取Class對(duì)象的方法集合,主要有三種方法:
-
getDeclaredMethods(): 返回類或接口聲明的所有方法:
- 包括公共,保護(hù),默認(rèn)(包)訪問(wèn)和私有方法
- 不包括繼承的方法
public Method[] getDeclaredMethods() throws SecurityException {}
-
getMethods(): 返回某個(gè)類所有的public方法
- 包括繼承類的public方法
public Method[] getMethods() throws SecurityException {}
-
getMethod(): 返回一個(gè)特定的方法
- 第一個(gè)參數(shù) :方法名稱
- 后面的參數(shù) :方法的參數(shù)對(duì)應(yīng)Class的對(duì)象
public Method getMethod(String name,Class<?>... parameterType) {}
- 獲取方法示例:
public class MethodTest { public static void methodTest() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> c = methodClass.class;
Object object = c.newInstance();
Method[] methods = c.getMethods();
Method[] declaredMethods = c.getDeclaredMethods(); // 獲取methodClass類中的add方法 Method method = c.getMethod("add", int.class, int.class); // getMethods()方法獲取的所有方法 System.out.println("getMethods獲取的方法:"); for (Method m:methods)
System.out.println(m); // getDeclaredMethods()方法獲取的所有方法 System.out.println("getDeclaredMethods獲取的方法:"); for (Method m:declaredMethods)
System.out.println(m);
}
} class methodClass { public final int n = 3; public int add(int a, int b) { return a + b;
} public int sub(int a, int b) { return a * b;
}
}
程序運(yùn)行結(jié)果:
getMethods獲取的方法: public int org.ScZyhSoft.common.methodClass.add(int,int) public int org.ScZyhSoft.common.methodClass.sub(int,int) public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
getDeclaredMethods獲取的方法: public int org.ScZyhSoft.common.methodClass.add(int,int) public int org.ScZyhSoft.common.methodClass.sub(int,int)
通過(guò)getMethods() 獲取的方法可以獲取到父類的方法
獲取構(gòu)造器信息
- 通過(guò)Class類的getConstructor方法得到Constructor類的一個(gè)實(shí)例
- Constructor類中newInstance方法可以創(chuàng)建一個(gè)對(duì)象的實(shí)例:
public T newInstance(Objec ... initargs)
newInstance方法可以根據(jù)傳入的參數(shù)來(lái)調(diào)用對(duì)應(yīng)的Constructor創(chuàng)建對(duì)象的實(shí)例
獲取類的成員變量信息
- getFileds: 獲取公有的成員變量
- getDeclaredFields: 獲取所有已聲明的成員變量,但是不能得到父類的成員變量
調(diào)用方法
- 從類中獲取一個(gè)方法后,可以使用invoke() 來(lái)調(diào)用這個(gè)方法
public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {}
- 示例:
public class InvokeTest { public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> klass = method.class; // 創(chuàng)建methodClass的實(shí)例 Object obj = klass.newInstance(); // 獲取methodClass的add方法 Method method = klass.getMethod("add", int.class, int.class); // 調(diào)用method對(duì)應(yīng)的方法,實(shí)現(xiàn)add(1,4) Object result = method.invoke(obj, 1, 4);
System.out.println(result);
}
} class methodClass { public final int n = 3; public int add(int a, int b) { return a + b;
} public int sub(int a,int b) { return a * b;
}
}
利用反射創(chuàng)建數(shù)組
- 數(shù)組是Java中一種特殊的數(shù)據(jù)類型,可以賦值給一個(gè)Object Reference
- 利用反射創(chuàng)建數(shù)組的示例:
public static void ArrayTest() throws ClassNotFoundException {
Class<?> cls = class.forName("java.lang.String");
Object array = Array.newInstance(cls, 25); // 在數(shù)組中添加數(shù)據(jù) Array.set(array, 0, "C");
Array.set(array, 1, "Java");
Array.set(array, 2, "Python");
Array.set(array, 3, "Scala");
Array.set(array, 4, "Docker"); // 獲取數(shù)據(jù)中的某一項(xiàng)內(nèi)容 System.out.println(Array.get(array, 3));
}
Array類是java.lang.reflect.Array類,通過(guò)Array.newInstance() 創(chuàng)建數(shù)組對(duì)象:
public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException { return newArray(componentType, length);
}
newArray方法是一個(gè)native方法,具體實(shí)現(xiàn)在HotSpot JVM中,源碼如下:
private static native Object newArray(Class<?> componentType, int length) throws NegativeArraySizeException;
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- newArray源碼目錄: openjdk\hotspot\src\share\vm\runtime\reflection.cpp
arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) { if (element_mirror == NULL) {
THROW_0(vmSymbols::java_lang_NullPointerException());
} if (length < 0) {
THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
} if (java_lang_Class::is_primitive(element_mirror)) {
Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL); return TypeArrayKlass::cast(tak)->allocate(length, THREAD);
} else {
Klass* k = java_lang_Class::as_Klass(element_mirror); if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {
THROW_0(vmSymbols::java_lang_IllegalArgumentException());
} return oopFactory::new_objArray(k, length, THREAD);
}
}
-
Array類的set和get方法都是native方法,具體實(shí)現(xiàn)在HotSpot JVM中,對(duì)應(yīng)關(guān)系如下:
- set: Reflection::array_set
- get: Reflection::array_get
invoke方法
- 在Java中很多方法都會(huì)調(diào)用invoke方法,很多異常的拋出多會(huì)定位到invoke方法:
java.lang.NullPointerException at ...... at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497)
invoke執(zhí)行過(guò)程
- invoke方法用來(lái)在運(yùn)行時(shí)動(dòng)態(tài)地調(diào)用某個(gè)實(shí)例的方法,實(shí)現(xiàn)如下:
@CallSensitive public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Refelection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; if (ma == null) {
ma = acquireMethodAccessor();
} return ma.invoke(obj, args);
}
權(quán)限檢查
-
AccessibleObject類是Field,Method和Constructor對(duì)象的基類:
- 提供將反射的對(duì)象標(biāo)記為在使用時(shí)取消默認(rèn)Java語(yǔ)言訪問(wèn)控制檢查的能力
-
invoke方法會(huì)首先檢查AccessibleObject的override屬性的值:
-
override默認(rèn)值為false:
- 表示需要權(quán)限調(diào)用規(guī)則,調(diào)用方法時(shí)需要檢查權(quán)限
- 也可以使用setAccessible() 設(shè)置為true
-
override如果值為true:
- 表示忽略權(quán)限規(guī)則,調(diào)用方法時(shí)無(wú)需檢查權(quán)限
- 也就是說(shuō),可以調(diào)用任意private方法,違反了封裝
-
- 如果override屬性為默認(rèn)值false,則進(jìn)行進(jìn)一步的權(quán)限檢查:
- 首先用Reflection.quickCheckMemberAccess(clazz, modifiers) 方法檢查方法是否為public
1.1 如果是public方法的話,就跳過(guò)本步
1.2 如果不是public方法的話,就用Reflection.getCallerClass()方法獲取調(diào)用這個(gè)方法的Class對(duì)象,這是一個(gè)native方法
@CallerSensitive public static native Class<?> getCallerClass();
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 在OpenJDK中可以找到getCallerClass方法的JNI入口-Reflection.c
JNIEXPORT jclass JNICALL Java_sun_reflect_Reflection_getCallerClass__ (JNIEnv *env, jclass unused) { return JVM_GetCallerClass(env, JVM_CALLER_DEPTH);
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- JVM_GetCallerClass的源碼位于jvm.cpp中
VM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env, int depth))
JVMWrapper("JVM_GetCallerClass"); // Pre-JDK 8 and early builds of JDK 8 don't have a CallerSensitive annotation; or // sun.reflect.Reflection.getCallerClass with a depth parameter is provided // temporarily for existing code to use until a replacement API is defined. if (SystemDictionary::reflect_CallerSensitive_klass() == NULL || depth != JVM_CALLER_DEPTH) {
Klass* k = thread->security_get_caller_class(depth); return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror());
} // Getting the class of the caller frame. // // The call stack at this point looks something like this: // // [0] [ @CallerSensitive public sun.reflect.Reflection.getCallerClass ] // [1] [ @CallerSensitive API.method ] // [.] [ (skipped intermediate frames) ] // [n] [ caller ] vframeStream vfst(thread); // Cf. LibraryCallKit::inline_native_Reflection_getCallerClass for (int n = 0; !vfst.at_end(); vfst.security_next(), n++) {
Method* m = vfst.method(); assert(m != NULL, "sanity"); switch (n) { case 0: // This must only be called from Reflection.getCallerClass if (m->intrinsic_id() != vmIntrinsics::_getCallerClass) {
THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "JVM_GetCallerClass must only be called from Reflection.getCallerClass");
} // fall-through case 1: // Frame 0 and 1 must be caller sensitive. if (!m->caller_sensitive()) {
THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), err_msg("CallerSensitive annotation expected at frame %d", n));
} break; default: if (!m->is_ignored_by_security_stack_walk()) { // We have reached the desired frame; return the holder class. return (jclass) JNIHandles::make_local(env, m->method_holder()->java_mirror());
} break;
}
} return NULL;
JVM_END
- 獲取Class對(duì)象caller后使用checkAccess方法進(jìn)行一次快速的權(quán)限校驗(yàn) ,checkAccess方法實(shí)現(xiàn)如下:
volatile Object securityCheckCache; void checkAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers) throws IllegalAccessException { if(caller == clazz){ // 快速校驗(yàn) return; // 權(quán)限通過(guò)校驗(yàn) }
Object cache = securityCheckCache; // 讀取volatile Class<?> targetClass = clazz; if (obj != null && Modifier.isProtected(modifiers) && ((targetClass = obj.getClass()) != clazz)) { // 必須匹配caller,targetClass中的一個(gè) if (cache instanceof Class[]) {
Class<?>[] cache2 = (Class<?>[]) cache; if (cache2[1] == targetClass && cache[0] == caller) { return; // 校驗(yàn)通過(guò) }
}
} else if (cache == caller) { return; // 校驗(yàn)通過(guò) }
slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
}
首先先執(zhí)行一次快速校驗(yàn),一旦Class正確則權(quán)限檢查通過(guò);如果未通過(guò),則創(chuàng)建一個(gè)緩存,中間再進(jìn)行檢查
- 如果上面所有的權(quán)限檢查都未通過(guò),將會(huì)執(zhí)行更詳細(xì)的檢查:
void slowCheckMemberAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers, Class<?> targetClass) throws IllegalAccessException {
Refelection.ensureMemberAccess(caller, clazz, obj, modifiers); // 如果成功,就更新緩存 Object cache = ((targetClass == clazz) ? caller : new Class<?>[] {caller, targetClass});
securityCheckCache = cache;
}
用Reflection.ensureMemberAccess方法繼續(xù)檢查權(quán)限.若檢查通過(guò)就更新緩存,這樣下一次同一個(gè)類調(diào)用同一個(gè)方法時(shí)就不用執(zhí)行權(quán)限檢查了,這是一種簡(jiǎn)單的緩存機(jī)制
由于JMM的happens-before規(guī)則能夠保證緩存初始化能夠在寫緩存之間發(fā)生,因此兩個(gè)cache不需要聲明為volatile
- 檢查權(quán)限的工作到此結(jié)束.如果沒(méi)有通過(guò)檢查就會(huì)拋出異常,如果通過(guò)檢查就會(huì)到下一步
調(diào)用MethodAccessor的invoke方法
- Method.invoke() 不是自身實(shí)現(xiàn)反射調(diào)用邏輯,而是通過(guò)sun.refelect.MethodAccessor來(lái)處理
-
Method對(duì)象的基本構(gòu)成:
- 每個(gè)Java方法有且只有一個(gè)Method對(duì)象作為root, 相當(dāng)于根對(duì)象,對(duì)用戶不可見(jiàn)
- 當(dāng)創(chuàng)建Method對(duì)象時(shí),代碼中獲得的Method對(duì)象相當(dāng)于其副本或者引用
- root對(duì)象持有一個(gè)MethodAccessor對(duì)象,所有獲取到的Method對(duì)象都共享這一個(gè)MethodAccessor對(duì)象
- 必須保證MethodAccessor在內(nèi)存中的可見(jiàn)性
- root對(duì)象及其聲明:
private volatile MethodAccessor methodAccessor; /**
* For sharing of MethodAccessors. This branching structure is
* currently only two levels deep (i.e., one root Method and
* potentially many Method objects pointing to it.)
*
* If this branching structure would ever contain cycles, deadlocks can
* occur in annotation code.
*/ private Method root;
- MethodAccessor:
/**
* This interface provides the declaration for
* java.lang.reflect.Method.invoke(). Each Method object is
* configured with a (possibly dynamically-generated) class which
* implements this interface
*/ public interface MethodAccessor { // Matches specification in {@link java.lang.reflect.Method} public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException;
}
MethodAccessor是一個(gè)接口,定義了invoke() 方法,通過(guò)Usage可以看出MethodAccessor的具體實(shí)現(xiàn)類:
- sun.reflect.DelegatingMethodAccessorImpl
- sun.reflect.MethodAccessorImpl
- sun.reflect.NativeMethodAccessorImpl
- 第一次調(diào)用Java方法對(duì)應(yīng)的Method對(duì)象的invoke()方法之前,實(shí)現(xiàn)調(diào)用邏輯的MethodAccess對(duì)象還沒(méi)有創(chuàng)建
- 第一次調(diào)用時(shí),才開(kāi)始創(chuàng)建MethodAccessor并更新為root, 然后調(diào)用MethodAccessor.invoke() 完成反射調(diào)用
/**
* NOTE that there is no synchronization used here.
* It is correct(though not efficient) to generate more than one MethodAccessor for a given Method.
* However, avoiding synchronization will probably make the implementation more scalable.
*/ private MethodAccessor acquireMethodAccessor() { // First check to see if one has been created yet, and take it if so MethodAccessor tmp = null; if (root != null)
tmp = root.getMethodAccessor(); if (tmp != null) {
methodAccessor = tmp;
} else {
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
} return tmp;
}
- methodAccessor實(shí)例由reflectionFactory對(duì)象操控生成 ,reflectionFactory是在AccessibleObject中聲明的:
/**
* Reflection factory used by subclasses for creating field,
* method, and constructor accessors. Note that this is called very early in the bootstrapping process.
*/ static final ReflectionFactory reflectionFactory = AccessController.doPrivileged( new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
- sun.reflect.ReflectionFactory方法:
public class ReflectionFactory { private static boolean initted = false; private static Permission reflectionFactoryAccessPerm = new RuntimePermission("reflectionFactoryAccess"); private static ReflectionFactory soleInstance = new ReflectionFactory(); // Provides access to package-private mechanisms in java.lang.reflect private static volatile LangReflectAccess langReflectAccess; /**
* "Inflation" mechanism. Loading bytecodes to implement Method.invoke() and Constructor.
* newInstance() currently costs 3-4x more than an invocation via native code for the first invocation (though subsequent invocations have been benchmarked to be over 20x faster)
* Unfortunately this cost increases startup time for certain applications that use reflection intensively (but only once per class) to bootstrap themselves
* To avoid this penalty we reuse the existing JVM entry points for the first few invocations of Methods and Constructors and then switch to the bytecode-based implementations
*/ // Package-private to be accessible to NativeMethodAccessorImpl and NativeConstructorAccessorImpl private static noInflation = false; private static int inflationThreshold = 15; // 生成MethodAccessor public MethodAccessor newMethodAccessor(Method method) {
checkInitted(); if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { return new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
} else {
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
acc.setParent(res); return res;
}
} /**
* We have to defer full initialization of this class until after the static initializer is run since java.lang.reflect
* Method's static initializer (more properly, that for java.lang.reflect.AccessibleObject) causes this class's to be run, before the system properties are set up
*/ private static void checkInitted() { if (initted) return;
AccessController.doPrivileged( new PrivilegedAction<Void>() { public Void run() { /**
* Tests to ensure the system properties table is fully initialized
* This is needed because reflection code is called very early in the initialization process (before command-line arguments have been parsed and therefore these user-settable properties installed
* We assume that if System.out is non-null then the System class has been fully initialized and that the bulk of the startup code has been run
*/ if (System.out == null) { // java.lang.System not yet fully initialized return null;
}
String val = System.getProperty("sun.reflect.noInflation"); if (val != null && val.equals("true")) {
noInflation = true;
}
val = System.getProperty("sun.reflect.inflationThreshold"); if (val != null) { try {
inflationThreshold = Integer.parseInt(val);
} catch (NumberFormatException e) { throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
}
}
initted = true; return null;
}
});
}
}
-
實(shí)際的MethodAccessor實(shí)現(xiàn)有兩個(gè)版本,一個(gè)是Java版本,一個(gè)是native版本,兩者各有特點(diǎn):
- 初次啟動(dòng)時(shí)Method.invoke() 和Constructor.newInstance() 方法采用native方法要比Java方法快3-4倍
- 啟動(dòng)后native方法又要消耗額外的性能而慢于Java方法
- Java實(shí)現(xiàn)的版本在初始化時(shí)需要較多時(shí)間,但長(zhǎng)久來(lái)說(shuō)性能較好
-
這是HotSpot的優(yōu)化方式帶來(lái)的性能特性:
- 跨越native邊界會(huì)對(duì)優(yōu)化有阻礙作用
-
為了盡可能地減少性能損耗,HotSpot JDK采用inflation方式:
- Java方法在被反射調(diào)用時(shí),開(kāi)頭若干次使用native版
- 等反射調(diào)用次數(shù)超過(guò)閾值時(shí)則生成一個(gè)專用的MethodAccessor實(shí)現(xiàn)類,生成其中的invoke() 方法的字節(jié)碼
- 以后對(duì)該Java方法的反射調(diào)用就會(huì)使用Java版本
-
ReflectionFactory.newMethodAccessor() 生成MethodAccessor對(duì)象的邏輯:
- native版開(kāi)始會(huì)會(huì)生成NativeMethodAccessorImpl和DelegatingMethodAccessorImpl兩個(gè)對(duì)象
- DelegatingMethodAccessorImpl:
/* Delegates its invocation to another MethodAccessorImpl and can change its delegate at run time */ class DelegatingMethodAccessorImpl extends MethodAccessorImpl { private MethodAccessorImpl delegate;
DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
setDelegate(delegate);
} public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { return delegate.invoke(obj, args);
} void setDelegate(MethodAccessorImpl delegate) { this.delegate = delegate;
}
}
DelegatingMethodAccessorImpl對(duì)象是一個(gè)中間層,方便在native版與Java版的MethodAccessor之間進(jìn)行切換
- native版MethodAccessor的Java方面的聲明: sun.reflect.NativeMethodAccessorImpl
/* Used only for the first few invocations of a Method; afterward,switches to bytecode-based implementation */ class NativeMethodAccessorImpl extends MethodAccessorImpl { private Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations;
NativeMethodAccessorImpl(Method method) { this.method = method;
} public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { /* We can't inflate methods belonging to vm-anonymous classes because that kind of class can't be referred to by name, hence can't be found from the generated bytecode */ if (++numInvocations > ReflectionFactory.inflationThreshold()
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
MethodAccessorImpl acc = (MethodAccessorImpl) new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
parent.setDelegate(acc);
} return invoke0(method, obj, args);
} void setParent(DelegatingMethodAccessorImpl parent) { this.parent = parent;
} private static native Object invoke0(Method m, Object obj, Object[] args);
}
- 每次NativeMethodAccessorImpl.invoke() 方法被調(diào)用時(shí),程序調(diào)用計(jì)數(shù)器都會(huì)增加1, 看看是否超過(guò)閾值
- 如果超過(guò)則調(diào)用MethodAccessorGenerator.generateMethod() 來(lái)生成Java版的MethodAccessor的實(shí)現(xiàn)類
- 改變DelegatingMethodAccessorImpl所引用的MethodAccessor為Java版
- 經(jīng)由DelegatingMethodAccessorImpl.invoke() 調(diào)用到的就是Java版的實(shí)現(xiàn)
JVM層invoke0方法
- invoke0方法是一個(gè)native方法,在HotSpot JVM里調(diào)用JVM_InvokeMethod函數(shù):
JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0 (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args) { return JVM_InvokeMethod(env, m, obj, args);
}
- openjdk/hotspot/src/share/vm/prims/jvm.cpp:
JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))
JVMWrapper("JVM_InvokeMethod");
Handle method_handle; if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) {
method_handle = Handle(THREAD, JNIHandles::resolve(method)); Handle receiver(THREAD, JNIHandles::resolve(obj)); objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));
oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL);
jobject res = JNIHandles::make_local(env, result); if (JvmtiExport::should_post_vm_object_alloc()) {
oop ret_type = java_lang_reflect_Method::return_type(method_handle()); assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!"); if (java_lang_Class::is_primitive(ret_type)) { // Only for primitive type vm allocates memory for java object. // See box() method. JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);
}
} return res;
} else {
THROW_0(vmSymbols::java_lang_StackOverflowError());
}
JVM_END
- 關(guān)鍵部分為Reflection::invoke_method: openjdk/hotspot/src/share/vm/runtime/reflection.cpp
oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) {
oop mirror = java_lang_reflect_Method::clazz(method_mirror); int slot = java_lang_reflect_Method::slot(method_mirror);
bool override = java_lang_reflect_Method::override(method_mirror) != 0; objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror)));
oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror);
BasicType rtype; if (java_lang_Class::is_primitive(return_type_mirror)) {
rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL);
} else {
rtype = T_OBJECT;
} instanceKlassHandle klass(THREAD, java_lang_Class::as_Klass(mirror));
Method* m = klass->method_with_idnum(slot); if (m == NULL) {
THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");
} methodHandle method(THREAD, m); return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD);
}
Java的對(duì)象模型 :klass和oop
Java版的實(shí)現(xiàn)
- Java版MethodAccessor的生成使用MethodAccessorGenerator實(shí)現(xiàn)
Generator for sun.reflect.MethodAccessor and sun.reflect.ConstructorAccessor objects using bytecodes to implement reflection. A java.lang.reflect.Method or java.lang.reflect.Constructor object can delegate its invoke or newInstance method to an accessor using native code or to one generated by this class. (Methods and Constructors were merged
together in this class to ensure maximum code sharing.)
運(yùn)用了asm動(dòng)態(tài)生成字節(jié)碼技術(shù) - sun.reflect.ClassFileAssembler
invoke總結(jié)
- invoke方法的過(guò)程:
-
MagicAccessorImpl:
- 原本Java的安全機(jī)制使得不同類之間不是任意信息都可見(jiàn),但JDK里面專門設(shè)了個(gè)MagicAccessorImpl標(biāo)記類開(kāi)了個(gè)后門來(lái)允許不同類之間信息可以互相訪問(wèn),由JVM管理
/** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl and
others, not because it actually implements an interface) is a
marker class in the hierarchy. All subclasses of this class are
"magically" granted access by the VM to otherwise inaccessible
fields and methods of other classes. It is used to hold the code
for dynamically-generated FieldAccessorImpl and MethodAccessorImpl
subclasses. (Use of the word "unsafe" was avoided in this class's
name to avoid confusion with {@link sun.misc.Unsafe}.) </P>
<P> The bug fix for 4486457 also necessitated disabling
verification for this class and all subclasses, as opposed to just
SerializationConstructorAccessorImpl and subclasses, to avoid
having to indicate to the VM which of these dynamically-generated
stub classes were known to be able to pass the verifier. </P>
<P> Do not change the name of this class without also changing the
VM's code. </P> */ class MagicAccessorImpl {
}
- @CallerSensitive注解
Summary: Improve the security of the JDK’s method-handle implementation by replacing the existing hand-maintained list of caller-sensitive methods with a mechanism that accurately identifies such methods and allows their callers to be discovered reliably.
/**
* A method annotated @CallerSensitive is sensitive to its calling class,
* via {@link sun.reflect.Reflection#getCallerClass Reflection.getCallerClass},
* or via some equivalent.
*
* @author John R. Rose
*/ @Retention(RetentionPolicy.RUNTIME) @Target({METHOD}) public @interface CallerSensitive {
}
-
用 @CallerSensitive注解修飾的方法從一開(kāi)始就知道具體調(diào)用此方法的對(duì)象
- 不用再經(jīng)過(guò)一系列的檢查就能確定具體調(diào)用此方法的對(duì)象
- 實(shí)際上是調(diào)用sun.reflect.Reflection.getCallerClass方法
-
Reflection類位于調(diào)用棧中的0幀位置
- sun.reflect.Reflection.getCallerClass() 方法返回調(diào)用棧中從0幀開(kāi)始的第x幀中的類實(shí)例
- 該方法提供的機(jī)制可用于確定調(diào)用者類,從而實(shí)現(xiàn)"感知調(diào)用者(Caller Sensitive)"的行為
- 即允許應(yīng)用程序根據(jù)調(diào)用類或調(diào)用棧中的其它類來(lái)改變其自身的行為
反射注意點(diǎn)
- 反射會(huì)額外消耗系統(tǒng)資源,如果不需要?jiǎng)討B(tài)地創(chuàng)建一個(gè)對(duì)象,就不要使用反射
- 反射調(diào)用方法時(shí)可以忽略權(quán)限檢查.可能會(huì)破壞封裝性而導(dǎo)致安全問(wèn)題
