1、從哪里入手開始分析反射的實現(xiàn)原理?
例如現(xiàn)在有這么一個代碼案例:
Person類:
public class Person {
public String role(String role){
System.out.println("role: " + role);
return role;
}
public String name(String name){
System.out.println("name: " + name);
return name;
}
}
測試類:
public class Gener {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("test.Person");
Method name = clazz.getMethod("name", String.class);
Person person = (Person)clazz.newInstance();
String personName = (String)name.invoke(person, new String[]{"tony"});
System.out.println(personName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
我們以方法的反射為例進行分析:
這行代碼作用是獲取Method對象:
Method name = clazz.getMethod("role", String.class);
根據(jù)方法名和參數(shù)類型獲取對應(yīng)的Method對象

private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
MethodArray interfaceCandidates = new MethodArray(2);
//獲取方法的Method對象
Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
//如果不為null,說明從本類或者父類中獲取到了root對象的拷貝對象,直接返回
if (res != null)
return res;
// Not found on class or superclass directly
//能夠執(zhí)行到這里,也就說明了沒有從本類以及父類中獲取到目標(biāo)方法的Method對象
interfaceCandidates.removeLessSpecifics();
//那么就從父接口返回的數(shù)組里查找,如果有,則返回Method對象,否則返回null
return interfaceCandidates.getFirst(); // may be null
}
getMethod0方法的邏輯:

跟進privateGetMethodRecursive方法:
private Method privateGetMethodRecursive(String name,
Class<?>[] parameterTypes,
boolean includeStaticMethods,
MethodArray allInterfaceCandidates) {
// Must _not_ return root methods
Method res;
// 先查找本類是否有這個方法
if ((res = searchMethods(privateGetDeclaredMethods(true),
name,
parameterTypes)) != null) {
if (includeStaticMethods || !Modifier.isStatic(res.getModifiers()))
return res;
}
//如果子類沒有的話,再從它的父類中找是不是有這個方法
if (!isInterface()) {
Class<? super T> c = getSuperclass();
if (c != null) {
if ((res = c.getMethod0(name, parameterTypes, true)) != null) {
return res;
}
}
}
//如果子類和父類中都找不到,那么再從父接口中查找
Class<?>[] interfaces = getInterfaces();
for (Class<?> c : interfaces)
if ((res = c.getMethod0(name, parameterTypes, false)) != null)
allInterfaceCandidates.add(res);
// 如果子類、父類和父接口中都找不到這個方法,那么就返回null
return null;
}
跟進privateGetDeclaredMethods方法看看:
我們先看下這個方法的注釋,通過注釋來了解方法的作用,注釋翻譯過來是:
這個方法返回一個Method的root對象數(shù)組。
數(shù)組里的這些root對象是不能被用戶感知的,它們的作用是:被用于通過
ReflectionFactory.copyMethod方法創(chuàng)建root對象的副本對象。

再來分析下這個方法的源碼:
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
//首先從緩存中拿數(shù)據(jù),獲取Method root,如果緩存不為空,則將緩存中滿足
//條件的各個方法對應(yīng)的Method root全部返回,以數(shù)組的形式
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
}
// No cached value available; request value from VM
//如果緩存中沒有滿足條件的Method root數(shù)據(jù)
//向虛擬機請求獲取本類中滿足條件的所有方法的Method root
//this表明是本類,getDeclaredMethods0(publicOnly)是個native方法,
//publicOnly即為向虛擬機請求的方法的過濾條件,是不是只需要獲取public的
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
//從VM獲取到數(shù)據(jù)后
if (rd != null) {
//將獲取到的數(shù)據(jù)添加到緩存
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
}
//返回獲取到的Method root
return res;
}
因為類中的每個方法所對應(yīng)的Method對象最初都是向VM請求獲得的,所以向VM請求獲得的那些Method對象,就是類中方法所對應(yīng)的Method root,即它們是各方法所對應(yīng)的Method對象的根對象,后面最終展現(xiàn)給開發(fā)者看得到的對象其實是通過對這些root對象進行拷貝得到的副本Method對象。
上一步獲取到Method root對象數(shù)組后,將這個數(shù)組作為searchMethods方法的參數(shù),跟進searchMethods方法看下:
它的邏輯是遍歷上一步獲取到的Method數(shù)組,逐個比較它們的與目標(biāo)方法的名稱、參數(shù)類型以及返回值類型是否一致,如果存在一致的,則通過res引用指向這個Method root對象,這個Method root對象就是本類中這個方法所對應(yīng)的根對象(直接向VM請求獲得的)。
最后遍歷完成后,比較res是否為null,如果為null,說明要反射的這個方法不存在,直接返回null,否則說明這個方法對應(yīng)的Method root是存在的,由res引用指向著,然后調(diào)用getReflectionFactory().copyMethod(res)對這個方法所對應(yīng)的Method root對象進行拷貝,得到這個root對象的副本對象,然后返回的是root對象的副本對象。

我們再看下copyMethod方法的邏輯,看看是如何對root對象進行拷貝的:
我們可以看到在copyMethod方法里調(diào)用的是傳入的root對象的copy()方法。

繼續(xù)跟進copy()方法里看下:

Method copy() {
// This routine enables sharing of MethodAccessor objects
// among Method objects which refer to the same underlying
// method in the VM. (All of this contortion is only necessary
// because of the "accessibility" bit in AccessibleObject,
// which implicitly requires that new java.lang.reflect
// objects be fabricated for each reflective call on Class
// objects.)
//由于這個方法是root對象調(diào)用的,root對象是分支結(jié)構(gòu)的第一層,即它本身
//就是根節(jié)點了,它上面沒有節(jié)點的了,所以它的root屬性必定是null
//如果不是null,說明這個對象不是root根對象,就不允許作為被拷貝的對象
if (this.root != null)
throw new IllegalArgumentException("Can not copy a non-root Method");
//因為root對象已經(jīng)是一個存在的對象,現(xiàn)在是直接使用它的這些屬性值來創(chuàng)建
//另一個對象,雖然對象地址不一樣了,但是對象的屬性值是一樣的,所以我們說
//res是root對象的副本對象。
Method res = new Method(clazz, name, parameterTypes, returnType,
exceptionTypes, modifiers, slot, signature,
annotations, parameterAnnotations, annotationDefault);
//創(chuàng)建了副本對象之后,因為它是由root對象拷貝而來的,所以將res的root引用
//指向this對象,即root對象。
res.root = this;
// Might as well eagerly propagate this if already present
//res的methodAccessor引用指向this,即root對象的methodAccessor對象。
res.methodAccessor = methodAccessor;
//然后root對象的副本就已經(jīng)創(chuàng)建好了,返回
return res;
}
查看Method類的成員屬性的話,我們可以看到有一個Method root屬性。它的作用是
指向它的根節(jié)點,記錄本Method對象是由哪個root根節(jié)點對象拷貝的。
關(guān)于Method類的root屬性的含義我們可以看下官方注釋:
// 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;
上面對root屬性的注釋翻譯過來就是:
在Method類中聲明一個Method類型的root屬性的目的是為了共享methodAccessor對象。Method分支結(jié)構(gòu)的層次目前只有兩層,例如第一層是Method root,第二層則是從root拷貝出來的其他的Method對象,并且它們的root引用都指向第一層的root對象。
如果第一層的root對象與第二層的Method對象之間存在環(huán)形引用的話,可能會發(fā)生死鎖。

———————————————————————————————————————————
獲取Method對象的流程分析完了之后我們獲得了一個Method方法,然后通過調(diào)用它的invoke方法實現(xiàn)目標(biāo)方法的調(diào)用,所以我們接著分析invoke方法的調(diào)用過程。
進入invoke方法:
先進行相關(guān)的權(quán)限檢查。
然后判斷ma是否為空,為空就創(chuàng)建一個MethodAccessor對象。
其中methodAccessor屬性使用了volatile關(guān)鍵字修飾,保證了它對所有線程的可見性。
private volatile MethodAccessor methodAccessor;

MethodAccessor是一個接口

MethodAccessor的實現(xiàn)類有3個

然后MethodAccessorImpl又是DelegatingMethodAccessorImpl和NativeMethodAccessorImpl的父類

這是創(chuàng)建MethodAccessor對象的邏輯,當(dāng)前對象的root屬性肯定不為null,因為當(dāng)前對象是一個root對象的副本對象,在前面的創(chuàng)建副本對象流程中我們知道,創(chuàng)建完副本對象后會將副本對象的root屬性指向root根節(jié)點Method對象,所以當(dāng)前對象的root屬性不為null,所以獲取root中的MethodAccessor對象,但是root根節(jié)點對象的MethodAccessor一開始是null的,所以獲取到的是null值,所以tmp!=null的邏輯就不通過了,所以就會調(diào)用newMethodAccessor方法創(chuàng)建MethodAccessor對象

可以看到newMethodAccessor方法剛開始有個checkInitted()邏輯

我們先看下checkInitted()的邏輯:
可以看到有獲取兩個系統(tǒng)屬性的邏輯:
System.getProperty("sun.reflect.noInflation");
System.getProperty("sun.reflect.inflationThreshold");
sun.reflect.noInflation這個參數(shù)的作用是決定是否啟用noinflation機制,ReflectionFactory.noInflation的默認(rèn)值是false,即默認(rèn)是有膨脹機制的。
所謂膨脹機制是指:一開始先使用native版本的MethodAccessor對象,等到native版本的調(diào)用次數(shù)達(dá)到sun.reflect.inflationThreshold所設(shè)定的閾值后,就會動態(tài)生成java版本的MethodAccessor對象來調(diào)用了。
而這個次數(shù)閾值就是通過sun.reflect.inflationThreshold這個系統(tǒng)參數(shù)設(shè)定的。
所以checkInitted()方法主要是獲取用戶設(shè)定的系統(tǒng)參數(shù)的值:sun.reflect.noInflation和sun.reflect.inflationThreshold。
如果用戶沒有設(shè)定,則使用默認(rèn)值:
ReflectionFactory.noInflation=false
ReflectionFactory.inflationThreshold=15

了解了newMethodAccessor方法的checkInitted()的邏輯后,我們再看下newMethodAccessor方法后面的邏輯:
創(chuàng)建NativeMethodAccessorImpl對象,然后把它作為參數(shù)傳給DelegatingMethodAccessorImpl的構(gòu)造方法,創(chuàng)建DelegatingMethodAccessorImpl對象

根據(jù)DelegatingMethodAccessorImpl的構(gòu)造方法邏輯:
this.setDelegate(var1);
從這里我們知道了,DelegatingMethodAccessorImpl里邊還有一個MethodAccessorImpl對象引用,而且它的invoke方法邏輯是調(diào)用的MethodAccessorImpl對象引用所指向的對象的invoke方法。
根據(jù)這個線路:通過一個對象來調(diào)用另一個對象,且這兩個對象中都定義了相同的方法,這不就是前面學(xué)過的典型的代理模式嗎。
所以我們知道了DelegatingMethodAccessorImpl的身份是作為一個代理類的,且由于代理類的邏輯已經(jīng)在源碼中實現(xiàn)了,所以這是一個靜態(tài)代理的典型應(yīng)用。
根據(jù)前面創(chuàng)建DelegatingMethodAccessorImpl代理對象時的傳參,我們知道它是將NativeMethodAccessorImpl對象傳給了DelegatingMethodAccessorImpl,并且把它賦予了MethodAccessorImpl對象引用,也就是說默認(rèn)情況下DelegatingMethodAccessorImpl一開始代理的是NativeMethodAccessorImpl類的對象。

而NativeMethodAccessorImpl中有一個DelegatingMethodAccessorImpl parent屬性,用于指向它被誰代理的。
因為在DelegatingMethodAccessorImpl類中有MethodAccessorImpl delegate屬性,它表示被代理的對象,那么DelegatingMethodAccessorImpl與NativeMethodAccessorImpl的關(guān)系可以描述為:
DelegatingMethodAccessorImpl代理NativeMethodAccessorImpl,DelegatingMethodAccessorImpl里存儲著它所代理的對象;
NativeMethodAccessorImpl被DelegatingMethodAccessorImpl代理,那么它里面存儲著它所對應(yīng)的代理對象。
numInvocations用于記錄調(diào)用nativeMethodAccessorImpl對象的調(diào)用次數(shù),它會與inflationThreshold進行比較,inflationThreshold的默認(rèn)值是15。
剛開始調(diào)用的代理對象調(diào)用的是NativeMethodAccessorImpl類對象,當(dāng)它的調(diào)用次數(shù)達(dá)到
inflationThreshold設(shè)定的閾值時就會動態(tài)創(chuàng)建一個java版本的目標(biāo)對象。
然后NativeMethodAccessorImpl類對象通過它里邊存儲的代理對象引用parent找到了代理對象,并調(diào)用代理對象的setDelegate(var3)方法更改被它代理的對象引用:
this.parent.setDelegate(var3);
也就是說一開始調(diào)用的是nativeMethodAccessorImpl對象,根據(jù)判斷邏輯是當(dāng)調(diào)用次數(shù)達(dá)到16次(閾值默認(rèn)是15次,超過15次,即在第16次就會動態(tài)創(chuàng)建java版本的目標(biāo)對象)時就會動態(tài)創(chuàng)建java版本的目標(biāo)對象,然后將java版本的對象作為新的被DelegatingMethodAccessorImpl代理的對象。
在第16次之后,即從第17次開始就不會再調(diào)用nativeMethodAccessorImpl對象的invoke方法了,而是調(diào)用的java版本的目標(biāo)對象的invoke方法。

如上所說,實際的MethodAccessor實現(xiàn)有兩個版本:
一個是Java實現(xiàn)的,另一個是native code實現(xiàn)的。
Java實現(xiàn)的版本在初始化時需要較多時間,但長久來說性能較好;
native版本正好相反,啟動時相對較快,但運行時間長了之后速度就比不過Java版了。
這是HotSpot的優(yōu)化方式帶來的性能特性,同時也是許多虛擬機的共同點:跨越native邊界會對優(yōu)化有阻礙作用,它就像個黑箱一樣讓虛擬機難以分析也將其內(nèi)聯(lián),于是運行時間長了之后反而是托管版本的代碼更快些。
為了權(quán)衡兩個版本的性能,Sun的JDK使用了“inflation”的技巧:讓Java方法在被反射調(diào)用時,開頭若干次使用native版,等反射調(diào)用次數(shù)超過閾值時則生成一個專用的MethodAccessor實現(xiàn)類,生成其中的invoke()方法的字節(jié)碼,以后對該Java方法的反射調(diào)用就會使用Java版。
可以在java啟動命令里加上-Dsun.reflect.noInflation=true,就可以把RefactionFactory的noInflation屬性設(shè)置為true了,這樣不用等到15次調(diào)用后,程序一開始就會用java版的MethodAccessor了。這個分析在上面已經(jīng)說過。
Sun的JDK是從1.4系開始采用這種優(yōu)化的。
創(chuàng)建了MethodAccessor對象后,newMethodAccessor方法返回

相當(dāng)于1步驟執(zhí)行完了,獲得了MethodAccessor對象,然后接著執(zhí)行setMethodAccessor(tmp)方法

把剛才創(chuàng)建的MethodAccessor對象賦予當(dāng)前Method對象的methodAccessor引用,并且判斷當(dāng)前Method對象的root屬性是否為null,肯定不為null的,因為它指向了root根節(jié)點Method對象,所以也會把剛才創(chuàng)建的MethodAccessor對象賦予root根節(jié)點對象的methodAccessor屬性。
然后我們的思路再回到Method對象的invoke方法

獲取到了MethodAccessor對象對象后,通過MethodAccessor對象來調(diào)用invoke方法,由于methodAccessor引用指向的是代理類DelegatingMethodAccessorImpl對象,所以實際上是調(diào)用DelegatingMethodAccessorImpl類的invoke方法,又由于DelegatingMethodAccessorImpl是代理類,實際上DelegatingMethodAccessorImpl的invoke方法調(diào)用的是被它代理的對象(native版本的或者java版本的)的invoke方法,最終才完成目標(biāo)方法的調(diào)用。
當(dāng)?shù)谝淮蝿?chuàng)建了類的某個方法的Method對象副本后,后面想要再創(chuàng)建這個方法的Method對象以及調(diào)用invoke方法的流程就比較簡單了:
因為第一次創(chuàng)建后,后面再創(chuàng)建的話,root方法會有緩存的,不需要再重新向VM請求了,直接從緩存拿到root對象。

如果要反射的方法確實存在,則res肯定非null,則對root對象進行拷貝

又會基于root根節(jié)點對象創(chuàng)建一個新的Method對象副本,且副本的root屬性還是指向root根節(jié)點對象,由于前面已經(jīng)創(chuàng)建過了副本,且創(chuàng)建了MethodAccessor對象,并且把MethodAccessor對象賦予了root根節(jié)點對象的methodAccessor引用,所以這里root根節(jié)點對象的methodAccessor引用不為null了,所以副本對象的methodAccessor引用直接初始化好了,全部指向第一次創(chuàng)建的MethodAccessor對象。

因為新的副本對象的methodAccessor屬性在前面創(chuàng)建的過程中已經(jīng)被初始化了,指向了第一次創(chuàng)建的那個MethodAccessor對象,所以這里的methodAccessor屬性是非null的了,所以不會再進入if語句塊重新創(chuàng)建MethodAccessor對象,直接調(diào)用MethodAccessor對象的invoke方法。

疑點解答:
為什么是第16次就會創(chuàng)建java版本的對象呢?

根據(jù)這個判斷邏輯,++this.numInvocations是先自增再與inflationThreshold閾值比較,所以結(jié)果是在第16次時,numInvocations的值才會大于inflationThreshold,才會執(zhí)行if語句塊。
If語句塊執(zhí)行結(jié)束后,還會最后一次nativeMethodAccessorImpl的invoke0方法。
所以,在所有的代理過程中,第16次所消耗的時間應(yīng)該是最長的,因為它包括了動態(tài)生成java對象的過程以及調(diào)用nativeMethodAccessorImpl對象的invoke0方法的過程。
而在這之前和之后的代理中要么只調(diào)用nativeMethodAccessorImpl的invoke0方法,要么調(diào)用java版本對象的invoke方法。
為什么要全部副本對象以及root對象的methodAccessor引用都指向同一個MethodAccessor對象呢?或者說為什么所有返回給用戶可見的Method對象都必須是root對象的副本對象呢?
我們上面分析說到,默認(rèn)情況下,代理對象會先調(diào)用native版本的methodAccessor對象,當(dāng)調(diào)用次數(shù)達(dá)到閾值后才會使用java版本的MethodAccessor對象。
如果我們每次創(chuàng)建的對象的methodAccessor屬性不是指向第一次創(chuàng)建的MethodAccessor對象,那么每個新創(chuàng)建的副本對象都得重新創(chuàng)建屬于自己的MethodAccessor對象對象,那么(在默認(rèn)情況下)每個副本對象又得經(jīng)歷一遍【先調(diào)用native MethodAccessor對象,調(diào)用次數(shù)達(dá)到閾值后再改為調(diào)用java版本的MethodAccessor對象】,這樣每個副本對象都得在native code和java code之間切換,性能損耗肯定更大,倒不如所有的副本對象都直接沿用之前已經(jīng)創(chuàng)建好的MethodAccessor對象,這樣就不需要每個副本對象都在native代碼和java代碼之間切換了,而是只需要當(dāng)這唯一的一個MethodAccessor對象的native版本的調(diào)用次數(shù)達(dá)到閾值后切換為java版本的就行了,由于只有唯一的一個MethodAccessor對象,所以對于同一個類的同一個方法來說,最多只需要切換一次。
因為每次創(chuàng)建同一個方法的Method對象,既然是同一個方法的,那么這個對象的所有內(nèi)容肯定應(yīng)該是相同的,所以所有的Method對象都需要從它的root對象拷貝而得這個也是合理的設(shè)計,再者,通過設(shè)定root節(jié)點對象,當(dāng)?shù)谝淮蝿?chuàng)建了MethodAccessor對象后,就可以將其保存到root對象中,后續(xù)再創(chuàng)建新的對象副本時,就不需要重新創(chuàng)建MethodAccessor對象了,而是直接從root對象獲取就行了,其目的也是上述所說的為了避免每個副本對象都需要經(jīng)歷從native MethodAccessor對象到j(luò)ava MethodAccessor對象的切換。
再者,前面也提到過,Method類的methodAccessor屬性是被volatile屬性修飾的。這說明什么呢?說明methodAccessor屬性可能會被多線程共享,它的變化需要保證對其他線程可見,所以通過volatile修飾,通過這里也能間接反映出methodAccessor對象不會創(chuàng)建多份,而是只有一份,然后被所有創(chuàng)建了這個方法副本對象的線程共享。

java反射源碼分析參考:
http://www.itdecent.cn/p/3ea4a6b57f87