在《一篇文章全面瞭解Java反射機制》中我們學習了Java反射的基本使用,這篇文章就帶大家一起來看看核心源碼。這可是與新手拉開差距的機會。
關於反射的類
關於反射的類是很多的,我們在基礎篇中已經涉及到一部分比如:Filed、Method、Constructor。同時,還有一些我們沒有看到的類,比如:AccessibleObject、ReflectionFactory、MethodAccessor等。
本篇文章我們重點介紹Method類的invoke方法的處理邏輯,這也是Java反射最核心的部分。
常見反射異常
我們在使用一些框架時經常會看到類似如下的異常:
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
這類異常便是通過反射機制實現的方法,在執行Method的invoke方法時拋出的異常。
比如,在Spring的xml配置文件中配置了不存在的類時,異常堆棧便會將異常指向調用的invoke方法。
所以,當你遇到類似的異常,可以簡單推斷一下,你所使用的框架可能使用了反射機制。
下面,我們就來看看Method的invoke方法到底做了些什麼。
源碼分析
直接點擊程序中調用的invoke方法,查看第一層源代碼:
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
@CallerSensitive註解:這個註解是Java修復漏洞用的。防止使用者使用雙重反射來提升權限,原理是因爲當時反射只檢查深度的調用者的類是否有權限,本身的類是沒有這麼高權限的,但是可以通過多重反射來提高調用的權限。
使用該註解,getCallerClass方法就會直接跳過有 @CallerSensitive修飾的接口方法,直接查找真實的調用者(actual caller)。
在invoke方法的前半部部分主要是用來做一些檢查工作,重點在於ma.invoke(obj, args)方法。這裏使用到了MethodAccessor接口,該接口位於sun.reflect包下,是生成反射類的入口,此部分屬於未開源部分。
在MethodAccessor中定義了invoke方法:
public interface MethodAccessor {
Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException;
}
該接口默認有三個實現類:
sun.reflect.DelegatingMethodAccessorImpl
sun.reflect.MethodAccessorImpl
sun.reflect.NativeMethodAccessorImpl
默認情況下methodAccessor值是爲null的,那麼看看acquireMethodAccessor方法是如何創建MethodAccessor的實現類的。
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 {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
return tmp;
}
acquireMethodAccessor方法中首先判斷是否存在MethodAccessor的實例,如果存在則直接拿來使用。否則,調用ReflectionFactory的newMethodAccessor方法來創建一個,創建完成並設置到root配置中。
繼續看newMethodAccessor的創建過程:
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;
}
}
通過debug會發現,默認情況下首先進入else處理邏輯中。在else中創建了一個NativeMethodAccessorImpl對象,並作爲構造參數傳入了DelegatingMethodAccessorImpl的構造方法中。
這裏很明顯使用了代理模式(可參看《Java代理模式及動態代理詳解》一文),將NativeMethodAccessorImpl對象交給 DelegatingMethodAccessorImpl對象代理。同時,通過setParent方法,NativeMethodAccessorImpl也持有了DelegatingMethodAccessorImpl的引用。
看你一下DelegatingMethodAccessorImpl的源碼,你會發現它就是代理模式的標準實現:
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被賦值給DelegatingMethodAccessorImpl中的DelegatingMethodAccessorImpl屬性,同時這兩個類都實現了MethodAccessorImpl接口。而在DelegatingMethodAccessorImpl又包裝了invoke方法。靜態代理的標準實現方式。
經過代碼跟蹤,我們發現ReflectionFactory類的newMethodAccessor方法返回的是DelegatingMethodAccessorImpl類對象。那麼ma.invoke()方法調用的是DelegatingMethodAccessorImpl的invoke方法。
而DelegatingMethodAccessorImpl又調用了設置的NativeMethodAccessorImpl對象的invoke方法。
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
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);
}
return invoke0(this.method, var1, var2);
}
該invoke方法中首先會判斷numInvocations是否會大於一個閾值,改值默認爲:
private static int inflationThreshold = 15;
如果大於該值並且不是匿名類則會進行新的MethodAccessorImpl的創建,並且賦值給代理類DelegatingMethodAccessorImpl。也就是說創建了一個新的實現類把上面原有的實現類給替換掉了。
在MethodAccessor的具體實現中使用了Inflation(通貨膨脹)機制。初次加載字節碼實現反射,使用Method.invoke()和Constructor.newInstance()加載花費的時間是使用原生代碼加載花費時間的3到4倍。這使得那些頻繁使用反射的應用需要花費更長的啓動時間。
爲了避免這種加載時間的問題,在第一次加載的時候重用了JVM的入口,之後切換到字節碼實現的實現。
上面我們也看到了MethodAccessor實現中有一個Native版本和Java版本。
Native版本一開始啓動快,但是隨着運行時間變長,速度變慢。Java版本一開始加載慢,但是隨着運行時間變長,速度變快。正是因爲兩種存在這些問題,所以第一次加載時使用的是NativeMethodAccessorImpl,而當反射調用次數超過15次之後,則使用MethodAccessorGenerator生成的MethodAccessorImpl對象去實現反射。
最後,我們看一下整個過程的時序圖。
原文鏈接:《超車時刻:Java反射源碼解析》
《Spring Boot 2.x 視頻教程全家桶》,精品Spring Boot 2.x視頻教程,打造一套最全的Spring Boot 2.x視頻教程。