SpringBoot實(shí)現(xiàn)使用反射模擬IOC和getBean
IOC就是spring的核心思想之一:控制反轉(zhuǎn)。這里不再贅述,看我的文章即可了解:
spring基礎(chǔ)思想IOC
其次就是java的反射,反射機(jī)制是spring的重要實(shí)現(xiàn)核心,今天我看spring的三級(jí)緩存解決循壞引用的問題時(shí),發(fā)現(xiàn)一個(gè)bean的生命周期與java對(duì)象的產(chǎn)生流程具備高度相似性,接著我就去重溫了一下bean的創(chuàng)建流程,發(fā)現(xiàn)一個(gè)bean實(shí)例從無到有經(jīng)歷的過程非常有意思,spring用極其優(yōu)雅的代碼實(shí)現(xiàn)了用反射和各種map數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)了bean的流水線式生產(chǎn),非常優(yōu)雅,于是我就嘗試用反射寫一個(gè)逆向生成實(shí)例對(duì)象的小玩意。
那么前置需要了解一個(gè)對(duì)象生成的過程:
我將對(duì)象的創(chuàng)建過程總結(jié)為:
檢查常量池是否存在該對(duì)象的符號(hào)引用并確定是否經(jīng)過類加載過程,都沒有則進(jìn)行類加載過程。
為新生對(duì)象分配內(nèi)存(兩種方式:指針碰撞和空閑列表<指針碰撞涉及到當(dāng)指針調(diào)動(dòng)頻繁時(shí)為了避免出現(xiàn)臟讀,采取本地線程分配緩沖TLAB的優(yōu)先分配情況>)并將除對(duì)象頭外的其他內(nèi)存空間賦值W為0。
設(shè)置對(duì)象頭。
對(duì)象的初始化,這個(gè)就是執(zhí)行你的構(gòu)造方法的過程,給你需要的字段賦值你想要定義的值。
補(bǔ)充一下其中的細(xì)節(jié):為新生對(duì)象分配內(nèi)存過程中,首先一個(gè)對(duì)象在類加載完成后它所需要的內(nèi)存大小是完全確定的,分配內(nèi)存的過程實(shí)際上就是在java堆里劃分一塊等大的內(nèi)存給它,但是該怎么劃分呢?如果java堆的內(nèi)存布局是嚴(yán)格的順序分配,即一邊是使用過的,一邊是空閑的,那么就會(huì)采取指針碰撞的方式分配內(nèi)存,所謂的指針在空閑區(qū)與使用區(qū)的分界線處,收到內(nèi)存需求時(shí),指針向后移動(dòng)直到移動(dòng)所覆蓋的長度等于java對(duì)象所需要的內(nèi)存大小時(shí)停止并進(jìn)行分配。但如果java堆的內(nèi)存布局是碎片化的不連續(xù)的呢?我們就只能維護(hù)一個(gè)列表,這個(gè)列表記錄了所有java堆空閑區(qū)的大小與位置信息,分配時(shí)只需要查找最適合新生對(duì)象的區(qū)域分配即可。
注意:java堆是否規(guī)整是由垃圾收集器的能力決定的,是否帶有空間壓縮整理的能力。當(dāng)我們采用的收集器是Serial與Parnew時(shí)是用指針碰撞的方式分配的,當(dāng)采用的是CMS垃圾收集器的時(shí)候,則是需要使用麻煩的空閑區(qū)表分配。
這里我們著重的去關(guān)注屬性與方法的填充即可:一個(gè)對(duì)象的靈魂就是它的屬性與方法:
整個(gè)工具用到的核心屬性:
private static volatile Constructor<?> constructor; private static volatile Object newInstance; private static volatile Map<String, Method> methodMap;
我們先看看這幾個(gè)方法的作用:
public static Constructor<?> getConstructor(Object dataType) { Class<?> typeClass = dataType.getClass(); try { Constructor<?> constructor = typeClass.getConstructor(); constructor.setAccessible(true); return constructor; } catch (NoSuchMethodException e) { e.printStackTrace(); return null; } }
獲取類型的構(gòu)造器,注意這可是無參構(gòu)造,如果你沒有無參構(gòu)造那么很有可能報(bào)錯(cuò),因?yàn)槲覀円膊恢浪卸嗌賹傩詫?duì)吧?(時(shí)刻記住咱們是逆向!??!不知道這個(gè)類型里有什么?。。∫磺卸际欠瓷鋷淼男畔ⅲ?/p>
public static void fillValueToNewInstance(Object dataType, Map<String, Object> initialMap) throws Exception { constructor = getConstructor(dataType); Class<?> typeClass = dataType.getClass(); Field[] declaredFields = typeClass.getDeclaredFields(); Iterator<Field> fieldIterator = Arrays.stream(declaredFields).iterator(); newInstance = constructor.newInstance(); while (fieldIterator.hasNext()) { Field field = fieldIterator.next(); field.setAccessible(true); if (initialMap != null) field.set(newInstance, initialMap.get(field.getName())); } }
獲取屬性并填充屬性值,這里也順帶著將屬性給進(jìn)去了。
public static Method[] getMethodArray(Object dataType) { return dataType.getClass().getDeclaredMethods(); }
獲取一切方法組成方法數(shù)組。
public static void fillMethodMap(Object dataType) { methodMap = new HashMap<>(); Method[] methodArray = getMethodArray(dataType); Iterator<Method> iterator = Arrays.stream(methodArray).iterator(); while (iterator.hasNext()) { Method method = iterator.next(); method.setAccessible(true); methodMap.put(method.getName(), method); } }
將方法存到方法集合中去存儲(chǔ)。
public static Object useMethod(String methodName, @Nullable Object... parameters) throws Exception { return methodMap.get(methodName).invoke(newInstance, parameters); }
使用方法要通過名稱。
@SneakyThrows public static Object getBean(Object dataType, Map<String, Object> parameterMap) { fillValueToNewInstance(dataType, parameterMap); fillMethodMap(dataType); return newInstance; }
getBean方法。
public static void main(String[] args) throws Exception { Map<String,Object> map = new HashMap<>(); map.put("name","xu"); map.put("age",Integer.valueOf(18)); map.put("sex",'女'); Person bean = (Person) getBean(new Person(), map); System.out.println(bean.toString()); System.out.println(useMethod("toString")); }
測試方法。類型信息如下:
class Person { private String name; private Integer age; private Character sex; //無參構(gòu)造絕對(duì)不能少 public Person() { } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; } }
測試結(jié)果如下:
這里我們可沒有用Person person = new Person();的方式實(shí)例化對(duì)象,用反射實(shí)現(xiàn)了對(duì)象的實(shí)例化。
里面用到關(guān)于反射的方法我列下來:
getDeclaredFields 獲取域?qū)傩詫?duì)象
getName 獲取屬性名稱
getType 獲取屬性類型的字節(jié)碼文件
setAccessible(true) 設(shè)置暴力破解,獲取對(duì)私有屬性的使用
getDeclaredMethods 獲取全部方法數(shù)組
getClass 獲取字節(jié)碼文件
getConstructor 獲取無參構(gòu)造器
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)使用反射模擬IOC和getBean的文章就介紹到這了,更多相關(guān)SpringBoot反射模擬IOC與getBean內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基礎(chǔ)之隱式轉(zhuǎn)換vs強(qiáng)制轉(zhuǎn)換
這篇文章主要介紹了Java基礎(chǔ)之隱式轉(zhuǎn)換vs強(qiáng)制轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2015-12-12Java后端對(duì)接微信支付(小程序、APP、PC端掃碼)包含查單退款
微信支付我們主要聚焦于這三種支付方式,其中JSPAI與APP主要與uniapp開發(fā)微信小程序與APP對(duì)接,本文主要介紹了Java后端對(duì)接微信支付(小程序、APP、PC端掃碼)包含查單退款,具有一定的參考價(jià)值,感興趣的可以了解一下2021-12-12Spring Boot定時(shí)器創(chuàng)建及使用解析
這篇文章主要介紹了Spring Boot定時(shí)器創(chuàng)建及使用解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07SpringBoot實(shí)現(xiàn)接口統(tǒng)一前綴
本文主要介紹了SpringBoot實(shí)現(xiàn)接口統(tǒng)一前綴,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07redis發(fā)布訂閱Java代碼實(shí)現(xiàn)過程解析
這篇文章主要介紹了redis發(fā)布訂閱Java代碼實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09SpringBoot基于RabbitMQ實(shí)現(xiàn)消息延遲隊(duì)列方案及使用場景
在很多的業(yè)務(wù)場景中,延時(shí)隊(duì)列可以實(shí)現(xiàn)很多功能,此類業(yè)務(wù)中,一般上是非實(shí)時(shí)的,需要延遲處理的,需要進(jìn)行重試補(bǔ)償?shù)?這篇文章主要介紹了SpringBoot基于RabbitMQ實(shí)現(xiàn)消息延遲隊(duì)列方案及使用場景,需要的朋友可以參考下2024-04-04把Java程序轉(zhuǎn)換成exe,可直接運(yùn)行的實(shí)現(xiàn)
這篇文章主要介紹了把Java程序轉(zhuǎn)換成exe,可直接運(yùn)行的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09MyBatis批量插入(insert)數(shù)據(jù)操作
本文給大家分享MyBatis批量插入(insert)數(shù)據(jù)操作知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-06-06