準備過年看下Spring源碼,用來唬人,哈哈哈哈。正經點,是爲了在遇到問題的時候,能知其然而知其因此然。可是在開始前,先惡補下基礎知識。今天看框架之魂——反射。html
反射是在編譯狀態,對某個類一無所知 ,但在運行狀態中,對於任意一個類,都能知道這個類的全部屬性和方法。java
這個說太乾澀了,沒有靈魂,就像下面兩張圖。git
因此咱來舉個例子,拒絕沒有靈魂。O(∩_∩)O哈哈~github
若是咱們沒有Orange類,那該類在編譯的時候就會報錯找不到該類。這是咱們日常使用的「正射」。這個名字是爲了和反射相對應,不是官方的術語。面試
可是這存在着一個明顯的缺點,就是在main方法裏調用的是Apple類,並無調用Orange類,因此應該是能夠正常調用的,當我想要調用Orange類的時候,再報錯便可。可是,事與願違,事情不是照着咱們的想法而發展的。緩存
咱們須要一種在編譯時不檢查類的調用狀況,只有在運行時,才根據相應的要求調用相應的類,這就是「反射」。安全
反射最重要的用途就是開發各類通用框架。不少框架(好比 Spring)都是配置化的(好比經過 XML 文件配置 Bean),爲了保證框架的通用性,它們可能須要根據配置文件加載不一樣的對象或類,調用不一樣的方法,這個時候就必須用到反射,運行時動態加載須要加載的對象。bash
舉一個例子,在運用 Struts 2 框架的開發中咱們通常會在 struts.xml
裏去配置 Action
,好比:app
<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
創建了一種映射關係,當 View 層發出請求時,請求會被
StrutsPrepareAndExecuteFilter
攔截,而後
StrutsPrepareAndExecuteFilter
會去動態地建立 Action 實例。好比咱們請求
login.action
,那麼
StrutsPrepareAndExecuteFilter
就會去解析struts.xml文件,檢索action中name爲login的Action,並根據class屬性建立SimpleLoginAction實例,並用invoke方法來調用execute方法,這個過程離不開反射。
萬事萬物都是對象。框架
Apple apple=new Apple();
中的apple爲Apple的一個實例。那Apple對象是哪一個的實例呢?
實際上是Class類的實例。
咱們能夠看他的註釋,私有的構造方法,只有JVM才能建立對象。
若是咱們能找到某個對象的Class類,便可以建立其實例。
(推薦使用)
這裏須要注意,經過類的全路徑名獲取Class對象會拋出一個異常,要用try....catch...捕獲異常。若是根據類路徑找不到這個類那麼就會拋出這個異常,Class類中forName方法源碼以下:
注:雖然寫了三種方式,但日常使用最多,最推薦的是第三種方式,由於第一種方式須要知道類,第二種方式須要知道實例,若是知道了這些,能夠直接調用其方法和參數,不必再用Class來實現功能。舉個例子,你從北京去上海,第一種方式直達就行,第二種方式和第三種方式則是先從北京到雲南,再從雲南到上海,顯得太冗餘。
咱們以Apple類爲例,利用發射來獲取其參數和方法。其有三個參數,默認default參數color,公有public參數size,私有private參數price。三個構造方法,分別是默認default構造,公有public帶有三個參數的有參構造,私有帶有兩個參數的有參構造。六個setter/getter方法公有方法,分別是color的默認default隔離級別的setter/getter方法,size的public隔離級別的setter/getter方法,price的private隔離級別的setter/getter方法。toString和三個參數的setter/getter方法。最後還有一個public隔離級別的toString方法。這樣詳細展開的描述,看起來很複雜,其實很簡單的,具體代碼以下:
package com.eastrobot.reflect;
public class Apple extends Fruit{
String color;//默認default
public int size;
private int price;
Apple() {//默認default
System.out.println("Apple的無參構造");
}
public Apple(String color, int size, int price) {
this.color = color;
this.size = size;
this.price = price;
System.out.println("Apple的有參構造——三個參數");
}
private Apple(String color, int size) {
this.color = color;
this.size = size;
this.price = 10;
System.out.println("Apple的有參構造——兩個參數");
}
@Override
public String toString() {
return "color:" + color + ",size:" + size + ",price:" + price;
}
//默認default
String getColor() {
return color;
}
public int getSize() {
return size;
}
private int getPrice() {
return price;
}
//默認default
void setColor(String color) {
this.color = color;
}
public void setSize(int size) {
this.size = size;
}
private void setPrice(int price) {
this.price = price;
}
}複製代碼
繼承的父類Fruit,包括一個public類型的參數taste,和其public類型的setter/getter方法。
public class Fruit {
public String taste;
public String getTaste() {
return taste;
}
public void setTaste(String taste) {
this.taste = taste;
}
}複製代碼
System.out.println("getDeclaredFields**********");
Field[] fields=appleClass.getDeclaredFields();
for(Field field:fields){
System.out.println(field.toString());
}複製代碼
運行結果以下:
注:無論何種隔離級別,getDeclaredFields都會獲取到全部參數。複製代碼
//指定參數
System.out.println("getDeclaredField**********");
Field colorField=appleClass.getDeclaredField("color");
System.out.println("color:"+colorField.toString());
Field sizeField=appleClass.getDeclaredField("size");
System.out.println("size:"+sizeField.toString());
Field priceField=appleClass.getDeclaredField("price");
System.out.println("price:"+priceField.toString());複製代碼
運行結果以下:
注:無論何種隔離級別,getDeclaredField能夠經過輸入值獲取指定參數。複製代碼
System.out.println("getFields**********");
Field[] fields=appleClass.getFields();
for(Field field:fields){
System.out.println(field.toString());
}複製代碼
運行結果以下:
注:只能經過反射獲取public類型的屬性,也包括繼承自父類的屬性。複製代碼
Field colorField=appleClass.getField("color");
System.out.println("color:"+colorField.toString());複製代碼
運行結果以下:
-------------------手動分割線-------------------
Field sizeField=appleClass.getField("size");
System.out.println("size:"+sizeField.toString());
複製代碼
運行結果以下:
-------------------手動分割線-------------------
Field priceField=appleClass.getField("price");
System.out.println("price:"+priceField.toString());複製代碼
運行結果以下:
注:只有public類型才能經過getField方法獲取到,其餘類型均獲取不到。複製代碼
看到這裏,有些小夥伴要問了,這是爲啥,理由呢?咱不能死記硬背,這樣過兩天就忘了,記得不牢固,咱來瞅瞅底層幹了啥。
咱們以getField爲例,觀察getDeclaredField和getField的區別,能夠看到二者都調用了privateGetDeclaredFields方法,可是區別是getDeclaredField方法中的參數publicOnly是false,getField方法中的參數publicOnly爲true。
getDeclaredField方法:
getField方法:
那privateGetDeclaredFields裏面幹了啥,咱們看下。
咱們能夠看到若是爲true,就取declaredPublicFields字段,即public字段,若是爲false,就取DeclaredFields。
//全部方法
System.out.println("getDeclaredMethods**********");
Method[] methods=appleClass.getDeclaredMethods();
for(Method method:methods){
System.out.println(method.toString());
}複製代碼
運行結果以下:
//指定方法
System.out.println("getDeclaredMethod**********");
//default
Method getColorMethod=appleClass.getDeclaredMethod("getColor");
System.out.println("getColorMethod:"+getColorMethod.toString());
//public
Method getSizeMethod=appleClass.getDeclaredMethod("getSize");
System.out.println("getSizeMethod:"+getSizeMethod.toString());
//private
Method getPriceMethod=appleClass.getDeclaredMethod("getPrice");
System.out.println("getPriceMethod:"+getPriceMethod.toString());
//父類的public
Method getTasteMethod=appleClass.getDeclaredMethod("getTaste");
System.out.println("getTasteMethod:"+getTasteMethod.toString());複製代碼
運行結果以下:
注:getDeclaredMethod只能獲取本身定義的方法,不能獲取從父類的方法。複製代碼
//全部方法
System.out.println("getMethods**********");
Method[] methods=appleClass.getMethods();
for(Method method:methods){
System.out.println(method.toString());
}複製代碼
運行結果以下:
注:getMethods能夠經過反射獲取全部的public方法,包括父類的public方法。複製代碼
//指定方法
System.out.println("getMethod**********");
Method method=appleClass.getMethod("toString");
System.out.println(method.toString());複製代碼
運行結果以下:
//構造方法
System.out.println("getDeclaredConstructors**********");
Constructor[] constructors=appleClass.getDeclaredConstructors();
for(Constructor constructor:constructors){
System.out.println(constructor.toString());
}複製代碼
運行結果以下:
//指定構造方法
System.out.println("getDeclaredConstructor**********");
Class[] cArg = new Class[3];
cArg[0] = String.class;
cArg[1] = int.class;
cArg[2] = int.class;
Constructor constructor=appleClass.getDeclaredConstructor(cArg);
System.out.println(constructor.toString());複製代碼
運行結果以下:
System.out.println("getConstructors**********");
Constructor[] constructors=appleClass.getConstructors();
for(Constructor constructor:constructors){
System.out.println(constructor.toString());
}複製代碼
運行結果:
//構造方法
System.out.println("getConstructor**********");
Constructor constructor1 = appleClass.getConstructor(String.class,int.class,int.class);
System.out.println("public類型的有參構造:" + constructor1.toString());
Constructor constructor2 = appleClass.getConstructor(String.class, int.class);
System.out.println("private類型的有參構造:" + constructor2.toString());複製代碼
運行結果:
//調用無參構造建立對象
Class appleClass = Class.forName("com.eastrobot.reflect.Apple");
Apple apple=(Apple)appleClass.newInstance();複製代碼
運行結果以下:
Constructor constructor=appleClass.getConstructor(String.class,int.class,int.class);
Apple apple=(Apple)constructor.newInstance("紅色",10,5);複製代碼
運行結果以下:
String name= appleClass.getName();
System.out.println("name:"+name);複製代碼
運行結果以下:
String simpleName =appleClass.getSimpleName();
System.out.println("simpleName:"+simpleName);複製代碼
運行結果以下:
//調用無參構造建立對象
Class appleClass = Class.forName("com.eastrobot.reflect.Apple");
//調用有參構造
Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class);
Apple apple = (Apple) constructor.newInstance("紅色", 10, 20);
//獲取toString方法並調用
Method method = appleClass.getDeclaredMethod("toString");
String str=(String)method.invoke(apple);
System.out.println(str);複製代碼
注:invoke+實例能夠調用相關public方法。
//調用無參構造建立對象
Class appleClass = Class.forName("com.eastrobot.reflect.Apple");
//調用有參構造
Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class);
Apple apple = (Apple) constructor.newInstance("紅色", 10, 20);
//獲取public的getSize方法並調用
Method method = appleClass.getDeclaredMethod("getSize");
System.out.println("getSize方法的isAccessible:" + method.isAccessible());
int size = (Integer) method.invoke(apple);
System.out.println("size:" + size);
//獲取private的getPrice方法並調用
method = appleClass.getDeclaredMethod("getPrice");
System.out.println("getPrice的isAccessible:" + method.isAccessible());
int price = (Integer) method.invoke(apple);
System.out.println("price:" + price);複製代碼
運行結果:
注:這樣一看,public和private類型的isAccessible都爲false,可是public類型的值能夠獲取到,可是private類型的值並不能獲取到。其實isAccessible()值爲 true或false,是指啓用和禁用訪問安全檢查的開關,若是爲true,則取消安全檢查,爲false,則執行安全檢查。如上,二者都爲false,說明二者的進行了安全檢查,getSize爲public類型,則能夠獲取值,而getPrice爲private,則不能獲取值。
//調用無參構造建立對象
Class appleClass = Class.forName("com.eastrobot.reflect.Apple");
//調用有參構造
Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class);
Apple apple = (Apple) constructor.newInstance("紅色", 10, 20);
//獲取price
Method otherMethod = appleClass.getDeclaredMethod("getPrice");
System.out.println("getPrice方法的isAccessible:" + otherMethod.isAccessible());
otherMethod.setAccessible(true);
int price = (Integer) otherMethod.invoke(apple);
System.out.println("以前的price:" + price);
//從新設置price
Method method = appleClass.getDeclaredMethod("setPrice", int.class);
System.out.println("isAccessible:" + method.isAccessible());
method.setAccessible(true);
method.invoke(apple, 100);
//再次獲取price
otherMethod = appleClass.getDeclaredMethod("getPrice");
otherMethod.setAccessible(true);
price = (Integer) otherMethod.invoke(apple);
System.out.println("以後的price:" + price);複製代碼
運行結果:
注:setAccessible(true)表示取消安全檢查,setAccessible(false)表示啓用安全檢查。
不同。由於有參構造方法也能夠反射,具體代碼以下:
Constructor constructor=appleClass.getConstructor(String.class,int.class,int.class);
Apple apple=(Apple)constructor.newInstance("紅色",10,5);複製代碼
優點:
在編譯時根本沒法知道該對象或類可能屬於哪些類,程序只依靠運行時信息來發現該對象和類的真實信息。反射提升了Java程序的靈活性和擴展性,下降耦合性,提升自適應能力。它容許程序建立和控制任何類的對象,無需提早硬編碼目標類。
劣勢:
使用反射基本上是一種解釋操做,用於字段和方法接入時要遠慢於直接代碼
。
使用反射會模糊程序內部邏輯
,程序人員但願在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術,於是會帶來維護問題。(這也就是看源碼爲何這麼難?哎。。。。)
由於反射不是硬編碼,在運行時能夠靈活發現該類的詳細信息,下降了代碼之間的耦合性。
怎麼去判斷一個函數的性能?由於函數的執行太快太快了,你須要一個放慢鏡,這樣才能捕捉到他的速度。怎麼作?把一個函數執行一百萬遍或者一千萬遍,你才能真正瞭解一個函數的性能。也就是,你若是想判斷性能,你就不能還停留在秒級,毫秒級的概念。
以下是將直接獲取實例,直接獲取方法,反射獲取實例,反射獲取方法分別執行1百萬次所花費差。
try {
//直接獲取實例
long startTime1 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
new Apple();
}
long endTime1 = System.currentTimeMillis();
System.out.println("直接獲取實例時間:" + (endTime1 - startTime1));
//直接獲取方法
long startTime2= System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
new Apple().toString();
}
long endTime2 = System.currentTimeMillis();
System.out.println("直接獲取方法時間:" + (endTime2- startTime2));
//反射獲取實例
Class appleClass=Class.forName("com.eastrobot.reflect.Apple");
long startTime3 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
appleClass.getDeclaredConstructor().newInstance();
}
long endTime3 = System.currentTimeMillis();
System.out.println("反射獲取實例:" + (endTime3 - startTime3));
//反射獲取方法
Apple apple= (Apple)appleClass.getDeclaredConstructor().newInstance();
long startTime4 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
Method method=appleClass.getMethod("toString");
method.invoke(apple);
}
long endTime4 = System.currentTimeMillis();
System.out.println("反射獲取方法:" + (endTime4 - startTime4));複製代碼
運行結果截圖:
咱們能夠看到反射的確會致使性能問題,但反射致使的性能問題是否嚴重跟使用的次數有關係,若是控制在100次之內,基本上沒什麼差異,若是調用次數超過了100次,性能差別會很明顯。
打個比方,若是快遞員就在你住的小區,那麼你報一個地址:xx棟xx號,那麼快遞員就能夠立刻知道你在哪裏,直接就去到你家門口;可是,若是快遞員是第一次來大家這裏,他是否是首先得查查百度地圖,看看怎麼開車過去,而後到了小區是否是得先問問物管xx棟怎麼找,而後,有可能轉在樓下轉了兩個圈纔到了你的門前。
咱們看上面這個場景,若是快遞員不熟悉你的小區,是否是會慢點,他的時間主要花費在了查找百度地圖,詢問物業管理。OK,反射也是同樣,由於我事先什麼都不知道,因此我得花時間查詢一些其餘資料,而後我才能找到你。
綜上,大部分咱們使用反射是不考慮性能的,日常使用的次數較少,若是真的遇到性能問題,如反射的效率影響到程序邏輯,能夠採用緩存或Java字節碼加強技術,參照庫有asm,也有第三方工具庫reflectAsm(github.com/EsotericSof…)。
setAccessible(true)取消了Java的權限控制檢查(注意不是改變方法或字段的訪問權限),對於setAccessible()方法是否會破壞類的訪問規則,產生安全隱患,見下:
咱們跟進Method的invoke方法,分爲兩步,一是語言訪問級別是否爲重寫,若是不是重寫則調用Reflection的quickCheckMemberAccess方法,即經過Modifiers 判斷是否具備訪問權限,quickCheckMemberAccess方法主要是簡單地判斷 modifiers 是否是 public,若是不是的話就返回 false。因此 protected、private、default 修飾符都會返回 false,只有 public 都會返回 true。若是爲false,則調用checkAccess方法。二是獲取MethodAccessor對象,並調用其invoke方法。
public final class Method extends AccessibleObject implements GenericDeclaration, Member {
private volatile MethodAccessor methodAccessor;
//每一個Java方法只有一個對應Method對象做爲root,這個root不會暴露給用戶,
//而是每次經過反射獲取Method對象時新建立的Method對象將root包裝起來。
private Method root;
@CallerSensitive
public Object invoke(Object obj, Object... args)throws IllegalAccessException, IllegalArgumentException,InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor;
//在第一次調用一個實際Java方法應該的Method對象的invoke方法以前
//實現調用邏輯的MethodAccessor對象尚未建立
//等到第一次調用時才建立MethodAccessor,並經過該MethodAccessor.invoke真正完成反射調用
if (ma == null) {
ma = acquireMethodAccessor();
}
//invoke並非本身實現的反射調用邏輯,而是委託給sun.reflect.MethodAccessor來處理
return ma.invoke(obj, args);
}
...
private MethodAccessor acquireMethodAccessor() {
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
//調用ReflectionFactory的newMethodAccessor方法,見下
tmp = reflectionFactory.newMethodAccessor(this);
//更新root,以便下次直接使用
setMethodAccessor(tmp);
}
return tmp;
}
...
void setMethodAccessor(MethodAccessor accessor) {
methodAccessor = accessor;
// Propagate up
if (root != null) {
root.setMethodAccessor(accessor);
}
}
複製代碼
Reflection類:
public static boolean quickCheckMemberAccess(Class<?> memberClass,
int modifiers)
{
return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers);
} 複製代碼
ReflectionFactory類:
private static boolean noInflation = false;
//選擇java版仍是C語言版的閾值
private static int inflationThreshold = 15;
public MethodAccessor newMethodAccessor(Method var1) {
checkInitted();
if (noInflation) {
//java版
return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
} else {
//c語言版
NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
var2.setParent(var3);
return var3;
}
}複製代碼
如上述代碼所示,實際的MethodAccessor實現有兩個版本,一個是Java實現的,另外一個是native code實現的。Java實現的版本在初始化時須要較多時間,但長久來講性能較好;native版本正好相反,啓動時相對較快,但運行時間長了以後速度就比不過Java版了。這是HotSpot的優化方式帶來的性能特性,同時也是許多虛擬機的共同點:跨越native邊界會對優化有阻礙做用,它就像個黑箱同樣讓虛擬機難以分析也將其內聯,因而運行時間長了以後反而是託管版本的代碼更快些。
爲了權衡兩個版本的性能,Sun的JDK使用了「inflation」的技巧:讓Java方法在被反射調用時,開頭若干次使用native版,等反射調用次數超過閾值時則生成一個專用的MethodAccessor實現類,生成其中的invoke()方法的字節碼,之後對該Java方法的反射調用就會使用Java版。C語言版的MethodAccessor主要涉及這NativeMethodAccessorImpl和DelegatingMethodAccessorImpl兩個類,而DelegatingMethodAccessorImpl是間接層,不是過重要,就不貼代碼啦。如下是NativeMethodAccessorImpl的代碼,核心是invoke方法:
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 {
if (++this.numInvocations > ReflectionFactory.inflationThreshold()) {
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);
}
void setParent(DelegatingMethodAccessorImpl var1) {
this.parent = var1;
}
private static native Object invoke0(Method var0, Object var1, Object[] var2);
}複製代碼
每次NativeMethodAccessorImpl.invoke()方法被調用時,都會增長一個調用次數計數器,看超過閾值沒有;一旦超過,則調用MethodAccessorGenerator.generateMethod()來生成Java版的MethodAccessor的實現類,而且改變DelegatingMethodAccessorImpl所引用的MethodAccessor爲Java版。後續經由DelegatingMethodAccessorImpl.invoke()調用到的就是Java版的實現了。
return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());複製代碼
Java的MethodAccessor主要涉及的是MethodAccessorGenerator類,具體代碼超長,只截取了部分代碼,主要有三個方法,直接就是上述的generateMethod方法,代碼以下:
public MethodAccessor generateMethod(Class var1, String var2, Class[] var3, Class var4, Class[] var5, int var6) {
return (MethodAccessor)this.generate(var1, var2, var3, var4, var5, var6, false, false, (Class)null);
}
private MagicAccessorImpl generate(final Class var1, String var2, Class[] var3, Class var4, Class[] var5, int var6, boolean var7, boolean var8, Class var9) {
ByteVector var10 = ByteVectorFactory.create();
this.asm = new ClassFileAssembler(var10);
this.declaringClass = var1;
this.parameterTypes = var3;
this.returnType = var4;
this.modifiers = var6;
this.isConstructor = var7;
this.forSerialization = var8;
this.asm.emitMagicAndVersion();
short var11 = 42;
boolean var12 = this.usesPrimitiveTypes();
if (var12) {
var11 = (short)(var11 + 72);
}
if (var8) {
var11 = (short)(var11 + 2);
}
var11 += (short)(2 * this.numNonPrimitiveParameterTypes());
this.asm.emitShort(add(var11, (short)1));
final String var13 = generateName(var7, var8);
this.asm.emitConstantPoolUTF8(var13);
this.asm.emitConstantPoolClass(this.asm.cpi());
this.thisClass = this.asm.cpi();
if (var7) {
if (var8) {
this.asm.emitConstantPoolUTF8("sun/reflect/SerializationConstructorAccessorImpl");
} else {
this.asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");
}
} else {
this.asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");
}
this.asm.emitConstantPoolClass(this.asm.cpi());
this.superClass = this.asm.cpi();
this.asm.emitConstantPoolUTF8(getClassName(var1, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
this.targetClass = this.asm.cpi();
short var14 = 0;
if (var8) {
this.asm.emitConstantPoolUTF8(getClassName(var9, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
var14 = this.asm.cpi();
}
this.asm.emitConstantPoolUTF8(var2);
this.asm.emitConstantPoolUTF8(this.buildInternalSignature());
this.asm.emitConstantPoolNameAndType(sub(this.asm.cpi(), (short)1), this.asm.cpi());
if (this.isInterface()) {
this.asm.emitConstantPoolInterfaceMethodref(this.targetClass, this.asm.cpi());
} else if (var8) {
this.asm.emitConstantPoolMethodref(var14, this.asm.cpi());
} else {
this.asm.emitConstantPoolMethodref(this.targetClass, this.asm.cpi());
}
this.targetMethodRef = this.asm.cpi();
if (var7) {
this.asm.emitConstantPoolUTF8("newInstance");
} else {
this.asm.emitConstantPoolUTF8("invoke");
}
this.invokeIdx = this.asm.cpi();
if (var7) {
this.asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");
} else {
this.asm.emitConstantPoolUTF8("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
}
this.invokeDescriptorIdx = this.asm.cpi();
this.nonPrimitiveParametersBaseIdx = add(this.asm.cpi(), (short)2);
for(int var15 = 0; var15 < var3.length; ++var15) {
Class var16 = var3[var15];
if (!isPrimitive(var16)) {
this.asm.emitConstantPoolUTF8(getClassName(var16, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
}
}
this.emitCommonConstantPoolEntries();
if (var12) {
this.emitBoxingContantPoolEntries();
}
if (this.asm.cpi() != var11) {
throw new InternalError("Adjust this code (cpi = " + this.asm.cpi() + ", numCPEntries = " + var11 + ")");
} else {
this.asm.emitShort((short)1);
this.asm.emitShort(this.thisClass);
this.asm.emitShort(this.superClass);
this.asm.emitShort((short)0);
this.asm.emitShort((short)0);
this.asm.emitShort((short)2);
this.emitConstructor();
this.emitInvoke();
this.asm.emitShort((short)0);
var10.trim();
final byte[] var17 = var10.getData();
return (MagicAccessorImpl)AccessController.doPrivileged(new PrivilegedAction<MagicAccessorImpl>() {
public MagicAccessorImpl run() {
try {
return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();
} catch (InstantiationException var2) {
throw (InternalError)(new InternalError()).initCause(var2);
} catch (IllegalAccessException var3) {
throw (InternalError)(new InternalError()).initCause(var3);
}
}
});
}
}
private static synchronized String generateName(boolean var0, boolean var1) {
int var2;
if (var0) {
if (var1) {
var2 = ++serializationConstructorSymnum;
return "sun/reflect/GeneratedSerializationConstructorAccessor" + var2;
} else {
var2 = ++constructorSymnum;
return "sun/reflect/GeneratedConstructorAccessor" + var2;
}
} else {
var2 = ++methodSymnum;
return "sun/reflect/GeneratedMethodAccessor" + var2;
}
}複製代碼
去閱讀源碼的話,能夠看到MethodAccessorGenerator是如何一點點把Java版的MethodAccessor實現類生產出來的,其實就是一個逐步解析的過程。
此時要注意的是最後的「sun/reflect/GeneratedMethodAccessor」+var2的代碼。
以上空說無用,太乾澀,咱來個例子。
public class Foo {
public void foo(String name) {
System.out.println("Hello, " + name);
}
}複製代碼
public class test {
public static void main(String[] args) {
try {
Class<?> clz = Class.forName("com.eastrobot.reflect.Foo");
Object o = clz.newInstance();
Method m = clz.getMethod("foo", String.class);
for (int i = 0; i < 17; i++) {
m.invoke(o, Integer.toString(i));
}
} catch (Exception e) {
}
}
}複製代碼
除了上述代碼,還須要在idea配置相關的運行參數,添加-XX:+TraceClassLoading參數,其爲要求打印加載類的監控信息。
咱們先用上述的例子執行下,運行結果以下,前面十五次是正常的,到第16次的時候,出現了不少打印信息,我已將一行標紅,「GeneratedMethodAccessor1」,這其實就是上面說的Java版獲取MethodAccessorGenerator的最後一行,1爲自增參數。當第17次的時候,就不會用Java版的方式從新獲取,而是直接複用啦。
終於結束了,邊玩邊寫,寫了五天,累死了,答應我,必定要好好看,好嗎?
若有說的不對地方,歡迎指正。
歡迎關注,咱一塊兒學習,麼麼噠。