1. 程式人生 > >java反射使用和源碼解析

java反射使用和源碼解析

rop 初始 沒有 factory dac arr 構造 lang port

1 反射

1.1 什麽是反射

正射:指的是我們知道類的定義和類中的方法名稱,直接先創建對象,然後通過對象去調用方法。例如:

Apple apple = new Apple(); //直接初始化,「正射」

apple.setPrice(4);

反射:指的是一開始不知道我要初始化的類對象是什麽,自然也無法使用 new 關鍵字來創建對象,需要用JDK 提供的反射 API 進行反射調用。需要通過類的路徑字符串創建對象,通過方法名稱字符串調用方法。例如:

Class clz = Class.forName("com.chenshuyi.reflect.Apple");//通過字符串獲取類Class

Constructor constructor = clz.getConstructor();//獲取類的構造器

Object object = constructor.newInstance();//創建對象

Method method = clz.getMethod("setPrice", int.class);//通過方法名獲取方法

method.invoke(object, 4);//調用方法;

第一段代碼在未運行時就已經確定了要運行的類(Apple),而第二段代碼則是在運行時通過字符串值才得知要運行的類(com.chenshuyi.reflect.Apple)。反射就是在運行時才知道要操作的類是什麽,並且可以在運行時獲取類的完整構造,並調用對應的方法。

1.2 反射獲取類、構造器、方法、屬性

(1)獲取類

使用 Class.forName 靜態方法。當你知道該類的全路徑名時,你可以使用該方法獲取 Class 類對象。

Class clz = Class.forName("java.lang.String");

(2)獲取構造器

通過 Constructor 對象的 newInstance() 方法

Constructor constructor = clz.getConstructor();//獲取類的構造器

Object object = constructor.newInstance();//創建對象

(1)

獲取方法

獲取方法的 Method 對象

Method setPriceMethod = clz.getMethod("setPrice", int.class);

利用 invoke 方法調用方法

setPriceMethod.invoke(appleObj, 14);

(2) 獲取屬性

過 Class 對象的 getFields() 方法可以獲取 Class 類的屬性,但無法獲取私有屬性。

Class clz = Apple.class;

Field[] fields = clz.getFields();

for (Field field : fields) {

System.out.println(field.getName());

}

輸出結果是:

Price

(5)獲取包含私有屬性

而如果使用 Class 對象的 getDeclaredFields() 方法則可以獲取包括私有屬性在內的所有屬性:

Class clz = Apple.class;

Field[] fields = clz.getDeclaredFields();

for (Field field : fields) {

System.out.println(field.getName());

}

輸出結果是:

name

price

1.3 反射的源碼解析

(1)Method的invoke方法

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);

}

(2)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;

}

(3)反射工廠返回對象reflectionFactory.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;// 最終返回的是DelegatingMethodAccessorImpl對象
}
}

其中DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
是將NativeMethodAccessorImpl var2對象作為入參,傳入DelegatingMethodAccessorImpl的構造函數。那麽構造函數內部做了什麽?很關鍵!

DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {

this.setDelegate(var1);對象設置在Delegate屬性中,後面會用到

}

delegate 屬性的定義是private MethodAccessorImpl delegate;

(4)步驟(3)最終返回的是DelegatingMethodAccessorImpl對象,所以步驟(1)中return ma.invoke(obj, args);函數invoke是DelegatingMethodAccessorImpl的方法。看看DelegatingMethodAccessorImpl的invoke方法定義如下:

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {

return this.delegate.invoke(var1, var2);//調用了屬性delegateinvoke方法;也就是NativeMethodAccessorImpl對象的invoke方法; }

(5)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);

}

每次NativeMethodAccessorImpl.invoke()方法被調用時,都會增加一個調用次數計數器numInvocations,看超過閾值 沒有;一旦超過,則調用MethodAccessorGenerator.generateMethod()來生成Java版的 MethodAccessor的實現類,並且通過this.parent.setDelegate(var3);改變DelegatingMethodAccessorImpl所引用的MethodAccessor為 Java版。後續經由DelegatingMethodAccessorImpl.invoke()調用到的就是Java版的實現了。

註意到關鍵的invoke0()方法是個native方法。它在HotSpot VM裏是由JVM_InvokeMethod()函數所支持的,不能在深入了:

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);

}

為什麽要這樣先用native後又切換為java的呢?

就像註釋裏說的,實際的 MethodAccessor 實現有兩個版本,一個是 Native 版本NativeMethodAccessorImpl,一個是 Java 版本MethodAccessorImpl。Java版本在第一次啟動時,需要加載字節碼實現Method.invoke() 和 Constructor.newInstance() ,比較耗時。所以Native 版本第一次啟動比java版本快3-4倍,但是後面的調用java版本比Native快20倍。所以為了避免啟動慢,第一次使用native版本快速啟動。為了避免後續運行慢,在切換到java版本MethodAccessorImpl 對象去實現反射。

java反射使用和源碼解析