Java反射機制在程式運行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性。這種動態的獲取信息以及動態調用對象的方法 的功能稱為java的反射機制。 首先你需要瞭解類載入的過程,這裡我們簡單提一下(載入-驗證-準備-解析-初始化),反射是靠JV ...
Java反射機制在程式運行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性。這種動態的獲取信息以及動態調用對象的方法 的功能稱為java的反射機制。
首先你需要瞭解類載入的過程,這裡我們簡單提一下(載入-驗證-準備-解析-初始化),反射是靠JVM和Class相關類實現的。
按照這個例子,我們調試下看看具體實現。
@Data public class Person { private String name; public static void main(String[] args) throws Exception { Person person = new Person(); person.setName("lewis"); for (int i = 0; i < 16; i++) { Method method = Person.class.getMethod("getName"); System.out.println(method.invoke(person)); } } }
@CallerSensitive public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { // 檢查方法是否為public if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); // 許可權校驗 checkAccess(caller, clazz, obj, modifiers); } } // MethodAccessor實現有兩個版本,一個是Java實現的,另一個是JNI實現的 MethodAccessor ma = methodAccessor; // read volatile if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); }
我們上面提到了 MethodAccessor 有兩個實現,Java版本和JNI版本(就是java native),
Java實現的版本在初始化時需要較多時間,但長久來說性能較好;JNI版本正好相反,啟動時相對較快,但運行時間長了之後速度就比不過Java版了。 為了儘可能地減少性能損耗,HotSpot JDK採用“inflation”的技巧:讓Java方法在被反射調用時,開頭若幹次使用JNI版,等反射調用次數超過閾值(15)時則生成一個專用的MethodAccessor實現類,生成其中的invoke()方法的位元組碼,以後對該Java方法的反射調用就會使用Java版本。 ReflectionFactory.newMethodAccessor()生產MethodAccessor對象的邏輯,一開始(JNI版)會生產NativeMethodAccessorImpl和DelegatingMethodAccessorImpl兩個對象。public MethodAccessor newMethodAccessor(Method var1) { checkInitted(); if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) { return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers()); } else { NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1); DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2); var2.setParent(var3); return var3; } }DelegatingMethodAccessorImpl的源碼如下: 這是一個中間層,方便在JNI版本與Java版的MethodAccessor之間實現切換。
class DelegatingMethodAccessorImpl extends MethodAccessorImpl { private MethodAccessorImpl delegate; DelegatingMethodAccessorImpl(MethodAccessorImpl var1) { this.setDelegate(var1); } public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { return this.delegate.invoke(var1, var2); } void setDelegate(MethodAccessorImpl var1) { this.delegate = var1; } }
來看看NativeMethodAccessorImpl實現,超過15次以後調用反射,就會通過我們上面提到的中間層 DelegatingMethodAccessorImpl 所引用的 MethodAccessor 都是java 版。
class NativeMethodAccessorImpl extends MethodAccessorImpl { private final Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; NativeMethodAccessorImpl(Method var1) { this.method = var1; } public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { // 方法被調用時,程式調用計數器都會增加1,看看是否超過閾值 if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) { // 超過15次 則調用MethodAccessorGenerator.generateMethod()來生成Java版的MethodAccessor的實現類 // 並且改變通過中間層,後續DelegatingMethodAccessorImpl所引用的MethodAccessor改為Java版 MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers()); this.parent.setDelegate(var3); } // native版本,JNI方式調用 return invoke0(this.method, var1, var2); } void setParent(DelegatingMethodAccessorImpl var1) { this.parent = var1; } private static native Object invoke0(Method var0, Object var1, Object[] var2); }
在預設情況下,方法的反射調用為委派實現,委派給本地實現來進行方法調用。在調用超過 15 次之後,委派實現便會將委派對象切換至動態實現。這個動態的位元組碼是在Java運行過程中通過ASM自動生成的,它將直接使用 invoke 指令來調用目標方法。
繼續查看代碼,可以看到sun.reflect.MethodAccessorGenerator#generate
的實現是調用asm位元組碼增強工具來生成類,此過程較長,不在此列出。在該方法的最後,我們發現有這樣一個操作sun.reflect.ClassDefiner#defineClass
,查看其源碼
static Class<?> defineClass(String name, byte[] bytes, int off, int len, final ClassLoader parentClassLoader) { // 創建一個DelegatingClassLoader用來載入生成的類 ClassLoader newLoader = AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { public ClassLoader run() { return new DelegatingClassLoader(parentClassLoader); } }); return unsafe.defineClass(name, bytes, off, len, newLoader, null); }
參考: