日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

依赖注入底层反射原理_Spring中反射与反射的原理(转载)

發(fā)布時(shí)間:2024/10/8 javascript 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 依赖注入底层反射原理_Spring中反射与反射的原理(转载) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Spring 中的反射與反射的原理 | Depp Wang's Blog

在造輪子:實(shí)現(xiàn)一個(gè)簡(jiǎn)易的 Spring IoC 容器一文中提到 Spring 在創(chuàng)建 Bean 實(shí)例和依賴注入時(shí)使用了反射,本文來(lái)具體分析一下 Spring 中的反射以及反射的原理。

一、Spring 中的反射

1.1、創(chuàng)建 Bean 實(shí)例時(shí)的反射// 通過(guò)類加載器,根據(jù) class 路徑,得到其類對(duì)象

Class> clz = Thread.currentThread().getContextClassLoader().loadClass("org.deppwang.litespring.v1.service.PetStoreService");

// 根據(jù)類對(duì)象生成 Bean 實(shí)例

return clz.newInstance();

反射體現(xiàn)在 clz.newInstance(); 中,核心代碼可分為兩部分:

1、利用反射獲取當(dāng)前類 PetStoreService 的所有構(gòu)造方法信息(Constructor 對(duì)象)

// java.lang.Class.java

// 調(diào)用 native 方法,此時(shí) publicOnly 為 false

res = getDeclaredConstructors0(publicOnly);

// native 方法,從 jvm 中的 class 文件中獲取構(gòu)造方法信息,再轉(zhuǎn)換為 Constructor 對(duì)象

private native Constructor[] getDeclaredConstructors0(boolean publicOnly);

2、利用反射通過(guò)默認(rèn)構(gòu)造方法生成實(shí)例

// sun.reflect.NativeConstructorAccessorImpl.java

// 調(diào)用 native 方法,var1 代表構(gòu)造方法的參數(shù),此時(shí)為 null

return newInstance0(this.c, var1);

// native 方法,真正生成實(shí)例的方法,執(zhí)行 class 文件的構(gòu)造方法

private static native Object newInstance0(Constructor> var0, Object[] var1);

1.2、構(gòu)造方法依賴注入時(shí)的反射// 通過(guò)反射獲取當(dāng)前類所有的構(gòu)造方法信息(Constructor 對(duì)象)

Constructor>[] candidates = beanClass.getDeclaredConstructors();

// 設(shè)置構(gòu)造方法參數(shù)實(shí)例

Object[] argsToUse = new Object[parameterTypes.length];

argsToUse[i] = getBean(beanNames.get(i));

// 使用帶有參數(shù)的 Constructor 對(duì)象實(shí)現(xiàn)實(shí)例化 Bean。此時(shí)使用反射跟上面一樣(newInstance0),只是多了參數(shù)

return constructorToUse.newInstance(argsToUse);

1.3、setter() 方法依賴注入時(shí)的反射// 通過(guò)反射獲取當(dāng)前類所有的方法信息(Method 對(duì)象)

Method[] methods = bean.getClass().getDeclaredMethods();

// 獲得方法參數(shù)實(shí)例

Object propertyBean = getBean(propertyName);

// 通過(guò)反射執(zhí)行調(diào)用 setter() 方法。invoke:調(diào)用方法,propertyBean 作為方法的參數(shù)

method.invoke(bean, propertyBean);

bean.getClass().getDeclaredMethods(); 中的核心代碼:

// java.lang.Class.java

// 調(diào)用 native 方法,publicOnly 為 false

getDeclaredMethods0(publicOnly);

// native 方法,從 jvm 中的 class 文件中獲取方法信息,再轉(zhuǎn)換為 Method

private native Method[] getDeclaredMethods0(boolean publicOnly);

method.invoke(bean, propertyBean); 中的核心代碼:

// sun.reflect.NativeMethodAccessorImpl.java

// 調(diào)用 native 方法,var1: bean、var2: propertyBean

return invoke0(this.method, var1, var2);

// native 方法,運(yùn)行 class 文件中的字節(jié)碼指令

private static native Object invoke0(Method var0, Object var1, Object[] var2);

1.4、@Autowired 依賴注入時(shí)的反射// 通過(guò)反射得到當(dāng)前類所有的字段信息(Field 對(duì)象)

Field[] fields = bean.getClass().getDeclaredFields();

// 判斷字段是否有 @Autowired 注解

Annotation ann = field.getAnnotation(Autowired.class);

// 設(shè)置字段可連接,相當(dāng)于將非 public(private、default、protect)更改為 public

field.setAccessible(true);

// 通過(guò)反射設(shè)置字段的值

field.set(bean, getBean(field.getName()));

bean.getClass().getDeclaredFields(); 中的核心代碼:

// java.lang.Class.java

// 調(diào)用 native 方法,此時(shí) publicOnly 為 false

getDeclaredFields0(publicOnly);

// native 方法,從 jvm 中獲取 class 文件的字段信息,再轉(zhuǎn)換為 Field

private native Field[] getDeclaredFields0(boolean publicOnly);

field.set(bean, getBean(field.getName())); 中的核心代碼:

// sun.reflect.UnsafeObjectFieldAccessorImpl.java

// 調(diào)用 native 方法,將目標(biāo)對(duì)象 var1 指定偏移量 fieldOffset 處的字段值設(shè)置(修改)為 var2。var1 為 bean, var2 為參數(shù)實(shí)例

unsafe.putObject(var1, this.fieldOffset, var2);

// sun.misc.Unsafe.java

// native 方法,直接修改堆中對(duì)象字段的數(shù)據(jù)

public native void putObject(Object var1, long var2, Object var4);

二、class 文件與類對(duì)象

class 文件由 java 文件編譯而來(lái),class 文件包含字段表、方法表、 方法(構(gòu)造方法)等。

當(dāng)類加載器將 class 文件加載進(jìn)虛擬機(jī)元空間(Meta-space,jdk 1.8)時(shí),虛擬機(jī)在元空間中創(chuàng)建一個(gè)與之對(duì)應(yīng)的類對(duì)象(Class 實(shí)例)。并將 class 文件由存放在磁盤(pán)的靜態(tài)結(jié)構(gòu)轉(zhuǎn)換為存放在內(nèi)存的運(yùn)行時(shí)結(jié)構(gòu)。

我們可以認(rèn)為一個(gè)類(class 文件)對(duì)應(yīng)一個(gè)類對(duì)象,當(dāng)前類的所有對(duì)象共用一個(gè)類對(duì)象。類對(duì)象作為訪問(wèn)存放在 jvm 的 class 文件的入口。

package java.lang;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

import java.lang.reflect.Constructor;

public final class Class{

private native Field[] getDeclaredFields0(boolean publicOnly);

private native Method[] getDeclaredMethods0(boolean publicOnly);

private native Constructor[] getDeclaredConstructors0(boolean publicOnly);

// ReflectionData 緩存反射對(duì)象

private static class ReflectionData{

volatile Field[] declaredFields;

volatile Field[] publicFields;

volatile Method[] declaredMethods;

volatile Method[] publicMethods;

volatile Constructor[] declaredConstructors;

volatile Constructor[] publicConstructors;

...

}

}

2.1、獲得類對(duì)象的方式// 1、通過(guò)對(duì)象

Class cls = object.getClass();

// Object.java

public final native Class> getClass();

// 2、通過(guò)類加載器

Class cls = Thread.currentThread().getContextClassLoader().loadClass("org.deppwang.litespring.v1.service.PetStoreService");

// 3、通過(guò) Class 類,本質(zhì)上也是通過(guò)類加載器

Class cls = Class.forName("org.deppwang.litespring.v1.service.PetStoreService");

// Class.java

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

ClassLoader loader,

Class> caller)

三、反射方法

以下是常用的反射方法。

3.1、Feild 相關(guān)Field[] fields = cls.getFields(); // 獲取所有公共的 Field(包括父類)

Field[] fields = cls.getDeclaredFields(); // 獲取當(dāng)前類的所有 Field(不包括父類),包括公共和非公共

Field field = cls.getDeclaredField("fieldName"); // 指定獲取當(dāng)前類某個(gè) Field

field.set(Object, Object); // 設(shè)置(修改)字段值

field.get(Object); // 獲取字段值

field.get(Object) 核心代碼:

// 調(diào)用 native 方法,獲取字段對(duì)應(yīng)的值

return unsafe.getObject(var1, this.fieldOffset);

// native 方法,從堆中獲取對(duì)象指定位置的對(duì)象

public native Object getObject(Object var1, long var2);

3.2、Method 相關(guān)Method[] methods = cls.getMethods(); // 獲取所有公共的 Method(包括父類)

Method[] methods = cls.getDeclaredMethods(); // 獲取當(dāng)前類的所有 Method(不包括父類),包括公共和非公共

method.invoke(Object instance, Object... parameters); // 運(yùn)行方法

運(yùn)行方法使用場(chǎng)景:要么是修改對(duì)象的數(shù)據(jù),如 void setter() 方法;要么是獲得執(zhí)行方法的返回結(jié)果。

String result = method.invoke().toString();

3.3、Constructor 相關(guān)Constructor>[] constructors = cls.getConstructors(); // 獲取所有公共的 Constructor(包括父類)

Constructor>[] constructors = cls.getDeclaredConstructors(); // 獲取當(dāng)前類的所有Constructor(不包括父類),包括公共和非公共

constructor.newInstance(Object... parameters); // 運(yùn)行構(gòu)造方法

當(dāng)沒(méi)有明確編寫(xiě)構(gòu)造方法,Java 編譯器將為該類構(gòu)建一個(gè)默認(rèn)構(gòu)造函數(shù)

四、native 方法

Java 1.1 新增「Java 本地接口」(Java Native Interface,JNI),JNI 是一種包容極廣的編程接口,允許我們從 Java 應(yīng)用程序里調(diào)用 native 方法,native 方法由其它語(yǔ)言(C 、C++ 或匯編語(yǔ)言等)編寫(xiě)。native 方法用于實(shí)現(xiàn) Java 無(wú)法處理的功能。

4.1、簡(jiǎn)單示例

一個(gè)在 Java 中使用 Java 本地接口(JNI)的簡(jiǎn)單示例。

環(huán)境:jdk8、macOS 10.15

// Main.java

public class Main{

public native int intMethod(int i);

static {

// 啟動(dòng)時(shí)載入 libMain.dylib

System.loadLibrary("Main");

}

public static void main(String[] args){

System.out.println(new Main().intMethod(2));

}

}

// Main.c:

// 將 Main.h 引入

#include "Main.h"

// 相當(dāng)于繼承 "Main.h" 的 Java_Main_intMethod

JNIEXPORT jint JNICALL Java_Main_intMethod(

JNIEnv *env, jobject obj, jint i)

{

return i * i;

}

編譯與運(yùn)行:

// 同時(shí)生成 Main.class 和 Main.h

javac Main.java -h .

// 根據(jù) Main.c 生成 libMain.dylib

gcc -dynamiclib -O3 \

-I/usr/include \

-I$JAVA_HOME/include \

-I$JAVA_HOME/include/darwin \

Main.c -o libMain.dylib

// 指定 library 的路徑為當(dāng)前路徑

java -cp . -Djava.library.path=$(pwd) Main

輸出:

4

/* Main.h .h 作為頭文件*/

/* DO NOT EDIT THIS FILE - it is machine generated */

#include

/* Header for class Main */

#ifndef _Included_Main

#define _Included_Main

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class: Main

* Method: intMethod

* Signature: (I)I

*/

JNIEXPORT jint JNICALL Java_Main_intMethod

(JNIEnv *, jobject, jint);

#ifdef __cplusplus

}

#endif

#endif

javac Main.java -h .

// 可拆分為兩個(gè)命令

javac Main.java

javah -jni Main

4.2、原理

運(yùn)行 Main.class 時(shí),將 libMain.dylib 載入虛擬機(jī),JVM 調(diào)用 libMain.dylib 的 Java_Main_intMethod,傳入?yún)?shù),libMain.dylib 由系統(tǒng)直接運(yùn)行,返回結(jié)果。

*env 用于將 java 類型數(shù)據(jù)與本地(此處為 C 語(yǔ)言)類型數(shù)據(jù)之間的轉(zhuǎn)換

jint 還是 Java 數(shù)據(jù)類型,Java 基本數(shù)據(jù)類型可以映射(使用),不用通過(guò) *env 轉(zhuǎn)換

/*C code*/

JNIEXPORT void JNICALL Java_ClassName_MethodName

(JNIEnv *env, jobject obj, jstring javaString)

{

/*Get the native string from javaString*/

const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0);

/*Do something with the nativeString*/

/*DON'T FORGET THIS LINE!!!*/

(*env)->ReleaseStringUTFChars(env, javaString, nativeString);

}

4.3、參考

五、總結(jié)

反射反射,哪里體現(xiàn)反射字面意思?

可以這么理解,通過(guò) native 方法得到反射對(duì)象,操作反射對(duì)象,像鏡子一樣,將反射到原對(duì)象上。

我們發(fā)現(xiàn),反射和 native 方法的關(guān)系:

獲取字段、方法、構(gòu)造方法對(duì)象,native() 方法實(shí)現(xiàn)

獲取字段值、設(shè)置修改字段值,native() 方法實(shí)現(xiàn)

運(yùn)行方法,native() 方法實(shí)現(xiàn)

運(yùn)行構(gòu)造方法,native() 方法實(shí)現(xiàn)

我們可以得出結(jié)論,反射由 native 方法實(shí)現(xiàn)。

我們說(shuō)通過(guò)反射實(shí)現(xiàn)一個(gè)功能,我們也可以說(shuō):

通過(guò)反射方法實(shí)現(xiàn)

通過(guò)反射 API 實(shí)現(xiàn)

通過(guò) native 方法實(shí)現(xiàn)

反射是一種非常規(guī)(native 方法實(shí)現(xiàn))方式獲取 class 文件信息、運(yùn)行 class 文件字節(jié)碼指令和操作對(duì)象數(shù)據(jù)的能力。

一句話總結(jié) :反射是一種運(yùn)行時(shí)獲取和修改對(duì)象數(shù)據(jù)的能力。

關(guān)于運(yùn)行時(shí):Java 是靜態(tài)語(yǔ)言,先編譯,后運(yùn)行。編譯時(shí)不執(zhí)行代碼,代碼都是運(yùn)行時(shí)執(zhí)行。

六、延伸閱讀

總結(jié)

以上是生活随笔為你收集整理的依赖注入底层反射原理_Spring中反射与反射的原理(转载)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。