反射&動(dòng)態(tài)代理

反射

Java 的動(dòng)態(tài)性體現(xiàn)在:反射機(jī)制涵叮、動(dòng)態(tài)執(zhí)行腳本語(yǔ)言萌踱、動(dòng)態(tài)操作字節(jié)碼

反射:在運(yùn)行時(shí)加載厌漂、探知哨啃、使用編譯時(shí)未知的類(lèi)烧栋。


Class.forName 使用的類(lèi)加載器是調(diào)用者的類(lèi)加載器

Class

表示 Java 中的類(lèi)型(class、interface拳球、enum审姓、annotation、primitive type祝峻、void)本身魔吐。


一個(gè)類(lèi)被加載之后,JVM 會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)該類(lèi)的 Class 對(duì)象莱找,類(lèi)的整個(gè)結(jié)構(gòu)信息會(huì)放在相應(yīng)

的 Class 對(duì)象中酬姆。

這個(gè) Class 對(duì)象就像一個(gè)鏡子一樣,從中可以看到類(lèi)的所有信息奥溺。

反射的核心就是 Class

如果多次執(zhí)行 forName 等加載類(lèi)的方法轴踱,類(lèi)只會(huì)被加載一次;一個(gè)類(lèi)只會(huì)形成一個(gè) Class

對(duì)象谚赎,無(wú)論執(zhí)行多少次加載類(lèi)的方法,獲得的 Class 都是一樣的诱篷。

用途


性能

反射帶來(lái)靈活性的同時(shí)壶唤,也有降低程序執(zhí)行效率的弊端

setAccessible 方法不僅可以標(biāo)記某些私有的屬性方法為可訪問(wèn)的屬性方法,并且可以提高程

序的執(zhí)行效率

實(shí)際上是啟用和禁用訪問(wèn)安全檢查的開(kāi)關(guān)棕所。如果做檢查就會(huì)降低效率闸盔;關(guān)閉檢查就可以提高

效率。

反射調(diào)用方法比直接調(diào)用要慢大約 30 倍琳省,如果跳過(guò)安全檢查的話比直接調(diào)用要慢大約 7 倍

開(kāi)啟和不開(kāi)啟安全檢查對(duì)于反射而言可能會(huì)差 4 倍的執(zhí)行效率迎吵。

為什么慢?

1)驗(yàn)證等防御代碼過(guò)于繁瑣,這一步本來(lái)在 link 階段针贬,現(xiàn)在卻在計(jì)算時(shí)進(jìn)行驗(yàn)證

2)產(chǎn)生很多臨時(shí)對(duì)象击费,造成 GC 與計(jì)算時(shí)間消耗

3)由于缺少上下文,丟失了很多運(yùn)行時(shí)的優(yōu)化桦他,比如 JIT(它可以看作 JVM 的重要評(píng)測(cè)標(biāo)準(zhǔn)

之一)

當(dāng)然蔫巩,現(xiàn)代 JVM 也不是非常慢了,它能夠?qū)Ψ瓷浯a進(jìn)行緩存以及通過(guò)方法計(jì)數(shù)器同樣實(shí)

現(xiàn) JIT 優(yōu)化,所以反射不一定慢圆仔。

實(shí)現(xiàn)

反射在 Java 中可以直接調(diào)用垃瞧,不過(guò)最終調(diào)用的仍是 native 方法,以下為主流反射操作的實(shí)

現(xiàn)坪郭。

Class.forName 的實(shí)現(xiàn)

Class.forName 可以通過(guò)包名尋找 Class 對(duì)象个从,比如 Class.forName("java.lang.String")。

在 JDK 的源碼實(shí)現(xiàn)中歪沃,可以發(fā)現(xiàn)最終調(diào)用的是 native 方法 forName0()嗦锐,它在 JVM 中調(diào)用的

實(shí)際是 FindClassFromCaller(),原理與 ClassLoader 的流程一樣绸罗。

public static Class<?> forName(String className)

throws ClassNotFoundException {

Class<?> caller = Reflection.getCallerClass();

return forName0(className, true, ClassLoader.getClassLoader(caller),

caller);

}

private static native Class<?> forName0(String name, boolean initialize,

ClassLoader loader,

Class<?> caller)

throws ClassNotFoundException;

Java_java_lang_Class_forName0(JNIEnv *env, jclass this, jstring classname,

jboolean initialize, jobject loader, jclass caller)

{

char *clname;

jclass cls = 0;

char buf[128];

jsize len;

jsize unicode_len;

if (classname == NULL) {

JNU_ThrowNullPointerException(env, 0);

return 0;

}

len = (*env)->GetStringUTFLength(env, classname);

unicode_len = (*env)->GetStringLength(env, classname);

if (len >= (jsize)sizeof(buf)) {

clname = malloc(len + 1);

if (clname == NULL) {

JNU_ThrowOutOfMemoryError(env, NULL);

return NULL;

}

} else {

clname = buf;

}

(*env)->GetStringUTFRegion(env, classname, 0, unicode_len, clname);

if (VerifyFixClassname(clname) == JNI_TRUE) {

/* slashes present in clname, use name b4 translation for exception */

(*env)->GetStringUTFRegion(env, classname, 0, unicode_len, clname);

JNU_ThrowClassNotFoundException(env, clname);

goto done;

}

if (!VerifyClassname(clname, JNI_TRUE)) { /* expects slashed name */

JNU_ThrowClassNotFoundException(env, clname);

goto done;

}

cls = JVM_FindClassFromCaller(env, clname, initialize, loader, caller);

done:

if (clname != buf) {

free(clname);

}

return cls;

}

JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name,

jboolean init, jclass from))

JVMWrapper2("JVM_FindClassFromClass %s", name);

if (name == NULL || (int)strlen(name) > Symbol::max_length()) {

// It's impossible to create this class; the name cannot fit

// into the constant pool.

THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);

}

TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);

oop from_class_oop = JNIHandles::resolve(from);

Klass* from_class = (from_class_oop == NULL)

? (Klass*)NULL

: java_lang_Class::as_Klass(from_class_oop);

oop class_loader = NULL;

oop protection_domain = NULL;

if (from_class != NULL) {

class_loader = from_class->class_loader();

protection_domain = from_class->protection_domain();

}

Handle h_loader(THREAD, class_loader);

Handle h_prot (THREAD, protection_domain);

jclass result = find_class_from_class_loader(env, h_name, init, h_loader,

h_prot, true, thread);

if (TraceClassResolution && result != NULL) {

// this function is generally only used for class loading during verification.

ResourceMark rm;

oop from_mirror = JNIHandles::resolve_non_null(from);

Klass* from_class = java_lang_Class::as_Klass(from_mirror);

const char * from_name = from_class->external_name();

oop mirror = JNIHandles::resolve_non_null(result);

Klass* to_class = java_lang_Class::as_Klass(mirror);

const char * to = to_class->external_name();

tty->print("RESOLVE %s %s (verification)\n", from_name, to);

}

return result;

JVM_END

getDeclaredFields 的實(shí)現(xiàn)

在 JDK 源 碼 中 意推, 可 以 知 道 class.getDeclaredFields() 方 法 實(shí) 際 調(diào) 用 的 是 native 方 法

getDeclaredFields0(),它在 JVM 主要實(shí)現(xiàn)步驟如下:

1)根據(jù) Class 結(jié)構(gòu)體信息珊蟀,獲取 field_count 與 fields[]字段菊值,這個(gè)字段早已在 load 過(guò)程中被

放入了

2)根據(jù) field_count 的大小分配內(nèi)存、創(chuàng)建數(shù)組

3)將數(shù)組進(jìn)行 forEach 循環(huán)育灸,通過(guò) fields[]中的信息依次創(chuàng)建 Object 對(duì)象

4)返回?cái)?shù)組指針

主要慢在如下方面:

創(chuàng)建腻窒、計(jì)算、分配數(shù)組對(duì)象

對(duì)字段進(jìn)行循環(huán)賦值

public Field[] getDeclaredFields() throws SecurityException {

checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);

return copyFields(privateGetDeclaredFields(false));

}

private Field[] privateGetDeclaredFields(boolean publicOnly) {

checkInitted();

Field[] res;

ReflectionData<T> rd = reflectionData();

if (rd != null) {

res = publicOnly ? rd.declaredPublicFields : rd.declaredFields;

if (res != null) return res;

}

// No cached value available; request value from VM

res = Reflection.filterFields(this, getDeclaredFields0(publicOnly));

if (rd != null) {

if (publicOnly) {

rd.declaredPublicFields = res;

} else {

rd.declaredFields = res;

}

}

return res;

}

private static Field[] copyFields(Field[] arg) {

Field[] out = new Field[arg.length];

ReflectionFactory fact = getReflectionFactory();

for (int i = 0; i < arg.length; i++) {

out[i] = fact.copyField(arg[i]);

}

return out;

}

Method.invoke 的實(shí)現(xiàn)

以下為無(wú)同步磅崭、無(wú)異常的情況下調(diào)用的步驟

1)創(chuàng)建 Frame

2)如果對(duì)象 flag 為 native儿子,交給 native_handler 進(jìn)行處理

3)在 frame 中執(zhí)行 java 代碼

4)彈出 Frame

5)返回執(zhí)行結(jié)果的指針

主要慢在如下方面:

需要完全執(zhí)行 ByteCode 而缺少 JIT 等優(yōu)化

檢查參數(shù)非常多,這些本來(lái)可以在編譯器或者加載時(shí)完成

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

}

NativeMethodAccessorImpl#invoke

public Object invoke(Object obj, Object[] args)

throws IllegalArgumentException, InvocationTargetException

{

// We can't inflate methods belonging to vm-anonymous classes because

// that kind of class can't be referred to by name, hence can't be

// found from the generated bytecode.

if (++numInvocations > ReflectionFactory.inflationThreshold()

&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {

MethodAccessorImpl acc = (MethodAccessorImpl)

new MethodAccessorGenerator().

generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers()); parent.setDelegate(acc); } return invoke0(method, obj, args); }

private static native Object invoke0(Method m, Object obj, Object[] args);

Java_sun_reflect_NativeMethodAccessorImpl_invoke0

(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)

{

return JVM_InvokeMethod(env, m, obj, args);

}

JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj,

jobjectArray args0))

JVMWrapper("JVM_InvokeMethod");

Handle method_handle;

if (thread->stack_available((address) &method_handle) >=

JVMInvokeMethodSlack) {

method_handle = Handle(THREAD, JNIHandles::resolve(method));

Handle receiver(THREAD, JNIHandles::resolve(obj));

objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));

oop result = Reflection::invoke_method(method_handle(), receiver, args,

CHECK_NULL);

jobject res = JNIHandles::make_local(env, result);

if (JvmtiExport::should_post_vm_object_alloc()) {

oop ret_type = java_lang_reflect_Method::return_type(method_handle());

assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!");

if (java_lang_Class::is_primitive(ret_type)) {

// Only for primitive type vm allocates memory for java object.

// See box() method.

JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);

}

}

return res;

} else {

THROW_0(vmSymbols::java_lang_StackOverflowError());

}

JVM_END

class.newInstance 的實(shí)現(xiàn)

1)檢測(cè)權(quán)限砸喻、預(yù)分配空間大小等參數(shù)

2)創(chuàng)建 Object 對(duì)象柔逼,并分配空間

3)通過(guò) Method.invoke 調(diào)用構(gòu)造函數(shù)(<init>())

4)返回 Object 指針

主要慢在如下方面:

參數(shù)檢查不能優(yōu)化或者遺漏

<init>()的查表

Method.invoke 本身耗時(shí)


public T newInstance()

throws InstantiationException, IllegalAccessException

{

if (System.getSecurityManager() != null) {

checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);

}

// NOTE: the following code may not be strictly correct under

// the current Java memory model.

// Constructor lookup

if (cachedConstructor == null) {

if (this == Class.class) {

throw new IllegalAccessException(

"Can not call newInstance() on the Class for java.lang.Class"

);

}

try {

Class<?>[] empty = {};

final Constructor<T> c = getConstructor0(empty, Member.DECLARED);

// Disable accessibility checks on the constructor

// since we have to do the security check here anyway

// (the stack depth is wrong for the Constructor's

// security check to work)

java.security.AccessController.doPrivileged(

new java.security.PrivilegedAction<Void>() {

public Void run() {

c.setAccessible(true);

return null;

}

});

cachedConstructor = c;

} catch (NoSuchMethodException e) {

throw (InstantiationException)

new InstantiationException(getName()).initCause(e);

}

}

Constructor<T> tmpConstructor = cachedConstructor;

// Security check (same as in java.lang.reflect.Constructor)

int modifiers = tmpConstructor.getModifiers();

if (!Reflection.quickCheckMemberAccess(this, modifiers)) {

Class<?> caller = Reflection.getCallerClass();

if (newInstanceCallerCache != caller) {

Reflection.ensureMemberAccess(caller, this, null, modifiers);

newInstanceCallerCache = caller;

}

}

// Run constructor

try {

return tmpConstructor.newInstance((Object[])null);

} catch (InvocationTargetException e) {

Unsafe.getUnsafe().throwException(e.getTargetException());

// Not reached

return null;

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市割岛,隨后出現(xiàn)的幾起案子愉适,更是在濱河造成了極大的恐慌,老刑警劉巖癣漆,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件维咸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惠爽,警方通過(guò)查閱死者的電腦和手機(jī)癌蓖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)婚肆,“玉大人租副,你說(shuō)我怎么就攤上這事〗闲裕” “怎么了附井?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵讨越,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我永毅,道長(zhǎng)把跨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任沼死,我火速辦了婚禮着逐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘意蛀。我一直安慰自己耸别,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布县钥。 她就那樣靜靜地躺著秀姐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪若贮。 梳的紋絲不亂的頭發(fā)上省有,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音谴麦,去河邊找鬼蠢沿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匾效,可吹牛的內(nèi)容都是我干的舷蟀。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼面哼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼野宜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起魔策,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤匈子,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后代乃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仿粹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年搁吓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吭历。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡堕仔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晌区,到底是詐尸還是另有隱情摩骨,我是刑警寧澤通贞,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站恼五,受9級(jí)特大地震影響昌罩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灾馒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一茎用、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧睬罗,春花似錦轨功、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至花盐,卻和暖如春羡滑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卒暂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工啄栓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人也祠。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓昙楚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诈嘿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子堪旧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容