深入了解Spring IoC

IoC全稱Inversion of Control即控制反轉,它還有一個別名依賴注入。spring利用Ioc容器幫我們自動構建對象及注入依賴對象,減少了對象構建與業務代碼的耦合,使得我們能夠更加高效愉快的寫bug🐞了( ̄▽ ̄)"。接下來我們詳細介紹下這個spring Ioc吧。

依賴注入原理

1.三種依賴注入方式

spring中有三種常見的依賴注入方式即:構造方法注入、setter方法注入、接口注入。其中前兩種注入方式是我們現在仍然用比較多的,而最后一種由于其需要侵入代碼,所以已經很少用了,這里就不介紹了。
(1) 構造方法注入:
這是我們經常能看到的注入方式,即通過對象構造器參數注入依賴對象。這種方式比較直觀,同時構造完成后對象即進入就緒狀態可以使用了。
(2)setter方法注入
在我們java bean對象中,經常會通過getter和setter方法獲取和設置對象的屬性,這些方法統稱為setter方法,通過為依賴對象添加setter方法,容器就會幫我們實現依賴對象的注入。其中我們比較常用的@Autowired就屬于這種方式。不過這種方式的注入,不能保證對象構造完成后就立馬進入就緒狀態。

在idea里當我們使用@Autowired時經常能看到"Filed injection is not recommended"的提示,告訴我們不推薦使用@Autowired進行注入。其主要原因是@Autowired的注入僅僅適用于Ioc容器,而當我們在程序中直接使用new去構造對象時,對象中的@Autowired依賴是無法自動注入的,就可能存在npe的風險。

2.BeanFactory和ApplicationContext

在講兩個主角之前,我們首先講下IoC Service Provider。上面介紹了三種依賴的注入方式,但我們需要的是相應的角色或服務來幫我們實際的實現對象的構建和與依賴的注入,而IoC Service Provider就是這個角色。它是一個抽象的概念,可能是一段代碼也可能是一組類,它主要負責業務對象的構建管理、業務對象間的依賴綁定。在spring中承擔這個角色的主要就是BeanFactory和ApplicationContext,當然他們也承擔著容器類的角色,我們這里只著重講解他們作為IoC Service Provider的功能,他們作為容器的功能會在下面講解。
這兩個類中BeanFactory比較古老了,它默認采用延遲加載策略,即只有當需要訪問容器中的受管對象時,才會對受管對象進行初始化及依賴注入操作,所以項目啟動比較快。而ApplicationContext是目前項目中比較常用的,它繼承了BeanFactory并增加了其他很多高級特性。ApplicationContext所管理的對象,默認在容器啟動之后全部進行初始化和綁定操作,所以其啟動速度會相對慢些,不過隨著spring和java的不斷優化和技術升級,這個啟動時間一般都可以接受,而其提供的很多特性非常大的方便了我們的開發,所以目前我們大部分的項目都是使用的ApplicationContext。

3.依賴注入過程

上面已經介紹了我們常用的注入方式及幫我實現注入的角色,接下來我們就可以介紹下依賴注入的過程了🤡🤠。
(1)首先對容器來說,要實現依賴注入它最需要的是對象的信息及對象間的依賴關系,spring通常會通過XML或注解等方式記錄這些信息。我們在一些比較古來的項目中還能看到這種XML配置文件,如下所示:

<bean id="djNewsProvider" class="..FXNewsProvider"> <property name="newsListener"><ref bean="djNewsListener"/></property><property name="newPersistener"><ref bean="djNewsPersister"/></property></bean>
<bean id="newsListener" class="..FXNewsProvider"> 
</bean>
<bean id="newPersistener" class="..FXNewsProvider"> 
</bean>

上面是我們在些老項目中常見的配置方式,在java5支持注解后,我們現在使用的更多是通過注解來代替這些XML配置。例如現在我們通過@Component、@Service等來標注對象信息,用@Autowired、@Resource來標注當前對象的依賴對象信息。ApplicationContext在項目啟動時會通過我們配置的scanning-path自動的去尋找這些對象并解析保存其信息及依賴關系。
(2)spring收集到對象信息和依賴關系后會將這些信息封裝到BeanDefinition中,每個容器中受管對象都會有一個BeanDefinition,它記錄了對象的所有必信息。包括對應的Class類型、是否為抽象類、構造方法參數及依賴關系等。最后這些數據會被注冊到BeanDefiniteRegistry中。
(3)當某個請求通過BeanFactory或ApplicationContext獲取對象時(getBean()),就會開始對象的實例化了。對象在實例化的過程中會先獲取對象的BeanDefinition,然后采用"策略模式",通過反射或者cglib動態代理來初始化對象(注意這里只是初始化,依賴對象還沒注入)。
(4)在完成初始化后容器會通過BeanWrapper包裹住實例,然后通過BeanWrapper來實現對象屬性值的設置和依賴的對象的注入。BeanWrapper根據實例的依賴對象到BeanFactory或ApplicationContext中獲取相關對象,然后set進對象,這樣就完成了實例的依賴注入。

我們可以再來看下(3)(4)的偽代碼:

// 通過反射構建對象
Object provider = Class.forName("...Provider").newInstance();
Object listener = Class.forName("...Listener").newInstance();BeanWrapper newProvider = new BeanWrapperImpl(provider);
// 注入依賴對象
newProvider.setPropertyValue("listener", listener);

整個(1)(2)(3)(4)就是容器中依賴注入的詳細流程了,是不是比想象中的要簡單呢ヘ|・?・|ノ*~●

Spring容器揭秘

IoC容器是Spring框架的重要組成部分。它通過加載配置數據并利用這些信息構造綁定容器內的所有對象,最終組裝成一個可用的基于輕量級容器的應用系統。spring容器功能的實現主要分為兩個過程:容器啟動階段Bean實例化階段

1.容器啟動階段

上面也大致介紹過了,實際上容器的啟動階段主要就是通過某些特定的工具類來收集配置信息,并將解析后的信息封裝為BeanDefinition,最后注冊到相應的BeanDefinitionRegistry中,總的來說就是進行對象管理信息的收集。
在這個階段spring為我們提供了一種叫BeanFactoryPostProcess的擴展機制來插手容器的啟動,它可以對容器中的BeanDefinition進行修改,比如修改Bean定義的屬性、為Bean增加其他信息等。
其中有一個使用的非常普遍的功能就是系統屬性值的替換:我們經常可以在項目文件中看到properties文件,這里面經常會存放數據庫密碼賬號等經常發生變化的配置數據,這些配置數據本來應該是在XML中配置的,但是我們通過${jdbc.url}這種方式將實際的數據配置在properties文件中。這個功能就是BeanFactoryPostProcess來幫我們實現,它修改了BeanDefinition的數據,用properties文件中數據替換了BeanDefinition中的${}內的數據。是不是很好玩🤣。

2.Bean實例化階段

容器啟動后并不會馬上實例化Bean,而是需要等到客戶端調用BeanFactory或ApplicationContext自動調用getBean方法獲取對象時才會真正的實例化bean。我們可以先看下Bean實例化的過程。在這里插入圖片描述
當客戶端調用了getBean方法后,就是開始了Bean的整體的實例化流程。

  1. 首先第一步進行的是對象的初始化,這個階段就是上面介紹過的通過cglib或者反射來初始化對象,這里只是初始化,依賴對象還沒注入。
  2. 這階段進行的是依賴對象的注入,依賴對象的注入我們在上面介紹過,主要是通過BeanWrapper來實現的。
  3. 在之后會檢查Aware相關接口并設置相關依賴,比如Bean名稱的設置,以及我們比較常用的ApplicationContextAware,這個Aware接口可以將容器類ApplicationContext的引用注入對象中,這樣我們就可以方便的通過ApplicationContext.getBean()獲取我們需要的對象了。
  4. 接下來是重要的BeanPostProcessor了,它分為Pre和Post兩種,在圖中可以看到,這兩個processor分別處在Bean實例化(調用構造方法)前和實例化(調用構造方法)后,它類似容器啟動階段的BeanFactoryPostProcessor可以用來動態修改擴展Bean的數據信息。其中一個最重要的應用就是Spring Aop的動態代理,Aop的動態代理就是通過BeanPostProcessor來實現的,它利用反射或cglib通過BeanPostProcessor生成了實例的代理對象并直接返回給了調用方完成了動態代理。
  5. 接下來在兩個BeanPostProcessor間,對象實例化(調用構造方法)前,spring會檢查對像是否實現了InitializingBean接口,如果是就會調用afterPropertiesSet()方法進一步調整對象狀態,除此之外我們常用的 @Bean(initMethod = “func”)中initMethod方法的執行也是在這里實現的。
  6. 最后階段就是在容器關閉Bean生命周期結束時,檢查Bean實現的destory-method,其中 @Bean(destroyMethod = “func”)中的destroyMethod方法也是在這里執行的。

好了,至此我們們就把容器的啟動及Bean的實例化講完了,舒服了🍑🍓🍎🍐。下面在簡單講下Bean的生命周期。

3.Bean的生命周期

容器除了會幫我們進行依賴注入外還會幫我們管理對象的生命周期,Bean的生命周期被稱為Scope,可以理解為對象的存活范圍或存活時間。bean的scope最常見的主要有兩種:singleton和prototype。
(1)singleton:
這種類型是我們最常見的。被標記為singleton scope的對象在容器中只存在一個實例,所有對該對象的引用都共享這個實例,它會一直存活到容器退出,幾乎和容器的生命周期一樣長。
(1)prototype:
被標記為prototype scope的對象,在容器每次收到該對象的請求時,都會生產一個新的對象返回給請求方,并且放回給請求方后,容器就不會再擁有當前對象的引用,請求方需要自己負責該對象的生命周期。這種類型很像我們自己在程序中使用new生成的對象,這中對象在我們不在使用后,會被GC線程在何時的時機回收掉,結束掉他的一生。

除此之外還有些不太常見的類型,原理都是一樣的,這里就不在贅述了。終于寫完了,嘻嘻嘻嘻嘻嘻嘻嘻嘻嘻,打完收工,吃飯去🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450947.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450947.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450947.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

軟文營銷實戰記錄

最近拜讀了徐茂權老師的《 網絡營銷決勝武器(第2版)》&#xff0c;下面會梳理書中的內容&#xff0c;記錄下以后可能會用到的軟文營銷的技巧。 一、軟文載體 1、平面媒體軟文&#xff1a;報紙、期刊。 2、非正式出版的基于印刷、打印形式載體的軟文&#xff1a;企業印刷的宣傳冊…

oracle中rownum和row_number()的區別

見&#xff1a;http://www.jb51.net/article/65960.htm row_number()over(partition by col1 order by col2)表示根據col1分組&#xff0c;在分組內部根據col2排序&#xff0c;而此函數計算的值就表示每組內部排序后的順序編號&#xff08;組內連續的唯一的&#xff09;。 與ro…

java類加載順序

在java中類的加載、初始化都是在程序運行期完成的&#xff0c;雖然會稍微增加開銷&#xff0c;但是卻很大的增加了靈活性&#xff0c;我們可用在運行期間動態的去網絡或其他地方加載一個二進制流來作為程序代碼的一部分。接下來我們簡單介紹下java類加載過程。 從上圖中我們可…

dealloc不調用的情況

2019獨角獸企業重金招聘Python工程師標準>>> 1、沒有停止定時器 - (void)dealloc { [_timer invalidate]; _timer nil; } 2、VC中有代理Delegate&#xff0c;需要設置delegate的時候&#xff0c;設置為weak property (nonatomic,weak) id<ZoeEatDe…

day10-列表生成式

列表生成式即List Comprehensions&#xff0c;是Python內置的非常簡單卻強大的可以用來創建list的生成式。 1、生成一個列表 a [i for i in range(1,100) if i%21]print(list(a))或print(a)[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, …

jrebel、JavaRebel

見&#xff1a;https://baike.baidu.com/item/jrebel/1115725?fraladdin JRebel是一套JavaEE開發工具。中文名jrebel屬 性JavaEE開發工具資 費收費軟件作 用Jrebel 可快速實現熱部署JRebel是一套JavaEE開發工具。JRebel允許開發團隊在有限的時間內完成更多的任務修正…

自己寫函數庫

大家現在寫 程序&#xff0c;是不是都是用新唐提供的函數庫&#xff1f;在體驗 開發板的一開始&#xff0c;我也是使用函數庫&#xff0c;畢竟這個太方便了。可是有一天&#xff0c;我發現一個只使用時鐘和IO以及 調試 串口的程序居然查過了16k的時候&#xff0c;我震驚了&…

[MicroPython]stm32f407控制DS18B20檢測溫度

2019獨角獸企業重金招聘Python工程師標準>>> 1.實驗目的 1. 學習在PC機系統中擴展簡單I/O 接口的方法。 2. 進一步學習編制數據輸出程序的設計方法。 3. 學習DS18B20的接線方法&#xff0c;并利用DS18B20檢測當前溫度。 2.所需元器件 F407Micropython開發板…

帶你理解Spring AOP

AOP概述 在我們的日常開發中&#xff0c;除了正常業務邏輯外&#xff0c;還可能經常會需要在業務邏輯的特定位置加入日志&#xff0c;以便于調試和問題分析。但是這種插入日志的邏輯和業務邏輯間并不存在連續性和依賴性&#xff0c;這種邏輯侵入隨著項目的不斷發展&#xff0c…

10.20隨筆

ES6 ECMAScript是一種由Ecma國際&#xff08;前身為歐洲計算機制造商協會,英文名稱是European Computer Manufacturers Association&#xff09;通過ECMA-262標準化的腳本程序設計語言。 這種語言在萬維網上應用廣泛&#xff0c;它往往被稱為JavaScript或JScript&#xff0c;但…

極客招募令!兄弟杯區塊鏈極客競技大賽在上海等您來戰!

據悉&#xff0c;由國內首家區塊鏈技術社區區塊鏈兄弟主辦&#xff0c;旺鏈科技、離子鏈、中國云體系產業創新戰略聯盟、無退社區、指旺金科等單位強力支持&#xff0c;HiBlock區塊鏈社區、火球財經、布洛克財經、海豚區塊鏈、區塊網等百家技術社區和媒體通力合作的兄弟杯區塊鏈…

Java中Web程序修改配置文件不重啟服務器的方法

見&#xff1a;http://blog.sina.com.cn/s/blog_69398ed9010191jg.html 另&#xff1a;http://ekisstherain.iteye.com/blog/1701463 jrebel 、JavaRebel是什么&#xff0c;見另一博客&#xff1a;jrebel/JavaRebel 開發環境 1. JDK 2. MyEclipse 3. Tomcat 4. Struts2 5.…

ffmpeg-0.6.3 移植到 windows 開源代碼

ffmpeg-0.6.3開源編碼解碼庫&#xff0c;從linux下移植到windows vs2005&#xff0c;全部開源。 需要 Intel C Compile 和 開源的SDL庫支持&#xff0c;由于 Intel C Compile支持C99語法&#xff0c;所以源代碼改動很小很小。 主要的修改 1&#xff1a;添加了linux中有而wind…

一起嘮嘮分布式鎖

&#xff08;1&#xff09;分布式鎖和分布式事務的區別 1.分布式鎖是在集群環境下&#xff0c;用來控制不同機器對全局共享資源的訪問。 2.分布式事務是在集群環境下&#xff0c;用來保證全局事務的一致性&#xff0c;保證多個數據庫的數據整體上能正確的從一個一致性狀態轉到…

luogu2577/bzoj1899 午餐 (貪心+dp)

首先&#xff0c;應該盡量讓吃飯慢的排在前面&#xff0c;先按這個排個序 然后再來決定每個人到底去哪邊 設f[i][j]是做到了第i個人&#xff0c;然后1號窗口目前的總排隊時間是j&#xff0c;目前的最大總時間 有這個i和j的話&#xff0c;再預處理出前i個人的排隊總時間sum[i]&a…

wpf中xps文檔合并功能實現

原文:wpf中xps文檔合并功能實現跟著上一篇的xps文檔套打的文章&#xff0c;近期一直在研究xps打印技術&#xff0c;其中用戶提到了一個需求&#xff0c;要求能夠多頁面進行打印&#xff0c;我的想法是&#xff0c;先生成xps文件&#xff0c;然后將文件讀取出來以后&#xff0c;…

DCT(離散余弦變換(DiscreteCosineTransform))

離散余弦變換&#xff08;Discrete Cosine Transform&#xff0c;簡稱DCT變換&#xff09;是一種與傅立葉變換緊密相關的數學運算。在傅立葉級數展開式中&#xff0c;如果被展開的函數是實偶函數&#xff0c;那么其傅立葉級數中只包含余弦項&#xff0c;再將其離散化可導出余弦…

從源碼看ConcurrentHashMap

簡介 ConcurrentHashMap是線程安全的HashMap實現&#xff0c;這里主要研究JDK8后的ConcurrentHashMap&#xff0c;下面是ConcurrentHashMap的簡單結構&#xff1a; ConcurrentHashMap基于HashMap的基本邏輯&#xff0c;通過CAS synchronized 來保證并發安全性。ConcurrentHas…

代碼重構的方法

見&#xff1a;http://blog.csdn.net/u011889786/article/details/51865344 見&#xff1a;http://blog.csdn.net/weiky626/article/details/1602691 一.提取子函數 說白了就是一個大函數里&#xff0c;可以根據不同功能分成幾個小函數&#xff0c;因為說不定&#xff0c;其他…

android 去掉標題欄、狀態欄、橫屏

// 去掉標題欄supportRequestWindowFeature(Window.FEATURE_NO_TITLE);// 全屏、隱藏狀態欄getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 橫屏setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION…