什么是IOC容器設(shè)計理念以及源碼怎么寫,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
我們是自2013年起的成都網(wǎng)站建設(shè)公司,提供網(wǎng)站建設(shè),電商網(wǎng)站設(shè)計開發(fā),成都外貿(mào)網(wǎng)站制作,響應(yīng)式網(wǎng)頁設(shè)計,成都微信小程序、等服務(wù)。為客戶創(chuàng)造有價值的品牌營銷體驗,讓互聯(lián)網(wǎng)提升企業(yè)的競爭力!
?、IOC核?理論回顧
知識點:
1. Ioc理念概要
2. 實體Bean的創(chuàng)建
3. Bean的基本特性
4. 依賴注?
1、Ioc理論概要
在JAVA的世界中,?個對象A怎么才能調(diào)?對象B?通常有以下?種?法。
類別描述時間點
外部傳?
構(gòu)造?法傳?創(chuàng)建引?對象時
屬性設(shè)置傳?設(shè)置對象狀態(tài)時
運?時做為參數(shù)傳?調(diào)?時
內(nèi)部創(chuàng)建
屬性中直接創(chuàng)建創(chuàng)建引?對象時
初始化?法創(chuàng)建創(chuàng)建引?對象時
運?時動態(tài)創(chuàng)建調(diào)?時
上表可以看到, 引??個對象可以在不同地點(其它引?者)、不同時間由不同的?法完
成。如果B只是?個?常簡單的對象 如直接new B(),怎樣都不會覺得復(fù)雜,?如你從來不會
覺得創(chuàng)建?個St ring 是?個件復(fù)雜的事情。但如果B 是?個有著復(fù)雜依賴的Service對象,這
時在不同時機引?B將會變得很復(fù)雜。
?時?刻都要維護B的復(fù)雜依賴關(guān)系,試想B對象如果項?中有上百過,系統(tǒng)復(fù)雜度將會成陪
數(shù)增加。
IOC容器的出現(xiàn)正是為解決這?問題,其可以將對象的構(gòu)建?式統(tǒng)?,并且?動維護對象的依
賴關(guān)系,從?降低系統(tǒng)的實現(xiàn)成本。前提是需要提前對?標(biāo)對象基于XML進?聲明。
2、實體Bean的構(gòu)建
a. 基于Class構(gòu)建
b. 構(gòu)造?法構(gòu)建
c. 靜態(tài)???法創(chuàng)建
d. Fact oryBean創(chuàng)建
1、基于ClassName構(gòu)建
這是最常規(guī)的?法,其原理是在spring底層會基于class 屬性 通過反射進?構(gòu)建。
2、構(gòu)造?法構(gòu)建
如果需要基于參數(shù)進?構(gòu)建,就采?構(gòu)造?法構(gòu)建,其對應(yīng)屬性如下:
name:構(gòu)造?法參數(shù)變量名稱
t ype:參數(shù)類型
index:參數(shù)索引,從0開始
value:參數(shù)值,spring 會?動轉(zhuǎn)換成參數(shù)實際類型值
ref :引?容串的其它對象
3、靜態(tài)???法創(chuàng)建
1
2
3
4
1
2
3
如果你正在對?個對象進?A/B測試 ,就可以采?靜態(tài)???法的?式創(chuàng)建,其于策略
創(chuàng)建不同的對像或填充不同的屬性。
該模式下必須創(chuàng)建?個靜態(tài)???法,并且?法返回該實例,spring 會調(diào)?該靜態(tài)?法
創(chuàng)建對象。
4、Fact oryBean創(chuàng)建
指定?個Bean??來創(chuàng)建對象,對象構(gòu)建初始化 完全交給該??來實現(xiàn)。配置Bean時指定該
??類的類名。
3、bean的基本特性
???? 作?范圍
???? ?命周期
???? 裝載機制
public static HelloSpring build(1 String type) {
2 if (type.equals("A")) {
3 return new HelloSpring("luban", "man");
4 } else if (type.equals("B")) {
5 return new HelloSpring("diaocan", "woman");
6 } else {
7 throw new IllegalArgumentException("type must A or B");
8 }
9 }
1
2
3
1 public class DriverFactoryBean implements FactoryBean {
2 private String jdbcUrl;
3 public Object getObject() throws Exception {
4 return DriverManager.getDriver(jdbcUrl);
5 }
6 public Class> getObjectType() {
7 return Driver.class;
8 }
9 public boolean isSingleton() {
10 return true;
11 }
12 public String getJdbcUrl() {
13 return jdbcUrl;
14 }
15 public void setJdbcUrl(String jdbcUrl) {
16 this.jdbcUrl = jdbcUrl;
17 }
18 }
a、作?范圍
很多時候Bean對象是?狀態(tài)的 ,?有些?是有狀態(tài)的 ?狀態(tài)的對象我們采?單例即可,?有
狀態(tài)則必須是多例的模式,通過scope 即可創(chuàng)建
scope=“prot ot ype”
scope=“singlet on”
如果?個Bean設(shè)置成 prot ot ype 我們可以 通過BeanFact oryAware 獲取 BeanFact ory 對象
即可每次獲取的都是新對像。
b、?命周期
Bean對象的創(chuàng)建、初始化、銷毀即是Bean的?命周期。通過 init -met hod、dest roymet
hod 屬性可以分別指定期構(gòu)建?法與初始?法。
如果覺得麻煩,可以讓Bean去實現(xiàn) Init ializingBean.af t erPropert iesSet ()、
DisposableBean.dest roy()?法。分別對應(yīng) 初始和銷毀?法
c、加載機制
指示Bean在何時進?加載。設(shè)置lazy-init 即可,其值如下:
t rue: 懶加載,即延遲加載
f alse:?懶加載,容器啟動時即創(chuàng)建對象
def ault :默認(rèn),采?def ault -lazy-init 中指定值,如果def ault -lazy-init 沒指定就是
f alse
什么時候使?懶加載?
懶加載會容器啟動的更快,??懶加載可以容器啟動時更快的發(fā)現(xiàn)程序當(dāng)中的錯誤 ,選擇哪
?個就看追求的是啟動速度,還是希望更早的發(fā)現(xiàn)錯誤,?般我們會選 擇后者。
4、依賴注?
試想IOC中如果沒有依賴注?,那這個框架就只能幫助我們構(gòu)建?些簡單的Bean,?之前所說
的復(fù)雜Bean的構(gòu)建問題將?法解決,spring這個框架不可能會像現(xiàn)在這樣成功。 spring 中
ioc 如何依賴注?呢。有以下?種?式:
1. set?法注?
2. 構(gòu)造?法注?
3. ?動注?(byName、byT ype)
4. ?法注?(lookup-met hod)
1、set?法注?
1 scope=“prototype
2
3
1
1
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
3
4 default-lazy-init="true">
2、構(gòu)造?法注?
3、?動注?(byName\byT ype\const ruct or)
byName:基于變量名與bean 名稱相同作為依據(jù)插?
byT ype:基于變量類別與bean 名稱作
const ruct or:基于IOC中bean 與構(gòu)造?法進?匹配(語義模糊,不推薦)
4、依賴?法注?(lookup-met hod)
當(dāng)?個單例的Bean,依賴于?個多例的Bean,?常規(guī)?法只會被注??次,如果每次都
想要獲取?個全新實例就可以采?lookup-met hod ?法來實現(xiàn)。
該操作的原理是基于動態(tài)代理技術(shù),重新?成?個繼承??標(biāo)類,然后重寫抽像?法到達注?
?的。
前?說所單例Bean依賴多例Bean這種情況也可以通過實現(xiàn) Applicat ionCont ext Aware 、
BeanFact oryAware 接?來獲取BeanFact ory 實例,從?可以直接調(diào)?get Bean?法獲取新實
例,推薦使?該?法,相?lookup-met hod語義邏輯更清楚?些。
?、IOC 設(shè)計原理與實現(xiàn)
2
3
1
2
3
4
5
1
2
1 #編寫一個抽像類
2 public abstract class MethodInject {
3 public void handlerRequest() {
4 // 通過對該抽像方法的調(diào)用獲取最新實例
5 getFine();
6 }
7 # 編寫一個抽像方法
8 public abstract FineSpring getFine();
9 }
10 // 設(shè)定抽像方法實現(xiàn)
11
12
13
知識點:
1、源碼學(xué)習(xí)的?標(biāo)
2、Bean的構(gòu)建過程
3、BeanFact ory與Applicat ionCont ext區(qū)別
1、源碼學(xué)習(xí)?標(biāo):
不要為了讀書?讀書,同樣不要為了閱讀源碼?讀源碼。沒有?的?頭扎進源碼的?森林
當(dāng)中很快就迷路了。到時就不是我們讀源碼了,?是源碼‘毒’我們。畢竟?個框架是由專業(yè)團
隊,歷經(jīng)N次版本迭代的產(chǎn)物,我們不能指望像讀?本書的?式去閱讀它。 所以必須在讀源碼
之前找到?標(biāo)。是什么呢?
?家會想,讀源碼的?標(biāo)不就是為了學(xué)習(xí)嗎?這種?標(biāo)太過抽像,?標(biāo)?法驗證。通常我
們會設(shè)定兩類型?標(biāo):?種是對源碼進?改造,?如添加修改某些功能,在實現(xiàn)這種?標(biāo)的過
程當(dāng)中?然就會慢慢熟悉了解該項?。但然這個難度較?,耗費的成本也?。另?個做法是
??提出?些問題,閱讀源碼就是為這些問題尋找答案。以下就是我們要?起在源碼中尋找答
案的問題:
1. Bean??是如何?產(chǎn)Bean的?
2. Bean的依賴關(guān)系是由誰解來決的?
3. Bean??和應(yīng)?上?的區(qū)別?
2、Bean的構(gòu)建過程
spring.xml ?件中保存了我們對Bean的描述配置,BeanFact ory 會讀取這些配置然后?
成對應(yīng)的Bean。這是我們對ioc 原理的?般理解。但在深??些我們會有更多的問題?
1. 配置信息最后是誰JAVA中哪個對象承載的?
2. 這些承載對象是誰業(yè)讀取XML?件并裝載的?
3. 這些承載對象?是保存在哪??
BeanDefinit ion (Bean定義)
ioc 實現(xiàn)中 我們在xml 中描述的Bean信息最后 都將保存?BeanDefinit ion (定義)對象中,
其中xml bean 與BeanDefinit ion 程?對?的關(guān)系。
由此可?,xml bean中設(shè)置的屬性最后都會體現(xiàn)在BeanDefinit ion中。如:
演示查看 BeanDefinit ion 屬性結(jié)構(gòu)
BeanDefinit ionRegist ry(Bean注冊器)
在上表中我們并沒有看到 xml bean 中的 id 和name屬性沒有體現(xiàn)在定義中,原因是ID 其作
為當(dāng)前Bean的存儲key注冊到了BeanDefinit ionRegist ry 注冊器中。name 作為別名key 注冊
到了 AliasRegist ry 注冊中?。其最后都是指向其對應(yīng)的BeanDefinit ion。
演示查看 BeanDefinit ionRegist ry屬性結(jié)構(gòu)
XML-bean BeanDefinit ion
class beanClassName
scope scope
lazy-init lazyInit
const ruct or-arg Const ruct orArgument
propert y MutablePropertyValues
f act ory-met hod f actoryMethodName
dest roy-met hod Abst ract BeanDefinit ion.dest royMet hodName
init -met hod Abst ract BeanDefinit ion.init Met hodName
aut owire Abst ract BeanDefinit ion.aut owireMode
id
name
????
????
BeanDefinit ionReader(Bean定義讀取)
?此我們學(xué)習(xí)了 BeanDefinit ion 中存儲了Xml Bean信息,?BeanDefinit ionRegist er 基于ID
和name 保存了Bean的定義。接下要學(xué)習(xí)的是從xml Bean到BeanDefinit ion 然后在注冊?
BeanDefinit ionRegist er 整個過程。
上圖中可以看出Bean的定義是由BeanDefinit ionReader 從xml 中讀取配置并構(gòu)建出
BeanDefinit ionReader,然后在基于別名注冊到BeanDefinit ionRegist er中。
查看BeanDefinit ???? ionReader結(jié)構(gòu)
?法說明:
???? loadBeanDefinit ions(Resource resource)
???? 基于資源裝載Bean定義并注冊?注冊器
???? int loadBeanDefinit ions(St ring locat ion)
???? 基于資源路徑裝載Bean定義并注冊?注冊器
???? BeanDefinit ionRegist ry get Regist ry()
???? 獲取注冊器
???? ResourceLoader get ResourceLoader()
???? 獲取資源裝載器
基于示例演示BeanDefinit ionReader裝載過程
Beanf act ory(bean ??)
有了Bean的定義就相當(dāng)于有了產(chǎn)品的配?,接下來就是要把這個配?送到??進??產(chǎn)了。
在ioc當(dāng)中Bean的構(gòu)建是由BeanFact ory 負責(zé)的。其結(jié)構(gòu)如下:
????
1 //創(chuàng)建一個簡單注冊器
2 BeanDefinitionRegistry register = new SimpleBeanDefinitionRegistry();
3 //創(chuàng)建bean定義讀取器
4 BeanDefinitionReader reader = new XmlBeanDefinitionReader(register);
5 // 創(chuàng)建資源讀取器
6 DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
7 // 獲取資源
8 Resource xmlResource = resourceLoader.getResource("spring.xml");
9 // 裝載Bean的定義
10 reader.loadBeanDefinitions(xmlResource);
11 // 打印構(gòu)建的Bean 名稱
12 System.out.println(Arrays.toString(register.getBeanDefinitionNames());
?法說明:
???? get Bean(St ring)
???? 基于ID或name 獲取?個Bean
????
???? 基于Bean的類別獲取?個Bean(如果出現(xiàn)多個該類的實例,將會報錯。但可以指定
primary=“t rue” 調(diào)整優(yōu)先級來解決該錯誤 )
???? Object get Bean(St ring name, Object ... args)
???? 基于名稱獲取?個Bean,并覆蓋默認(rèn)的構(gòu)造參數(shù)
???? boolean isT ypeMat ch(St ring name, Class> t ypeT oMat ch)
???? 指定Bean與指定Class 是否匹配
`
以上?法中重點要關(guān)注get Bean,當(dāng)?戶調(diào)?get Bean的時候就會觸發(fā) Bean的創(chuàng)建動作,其
是如何創(chuàng)建的呢?
演示基???? 本BeanFactory獲取?個Bean
1 #創(chuàng)建Bean堆棧
2 // 其反射實例化Bean
3 java.lang.reflect.Constructor.newInstance(Unknown Source:-1)
4 BeanUtils.instantiateClass()
5 //基于實例化策略 實例化Bean
6 SimpleInstantiationStrategy.instantiate()
7 AbstractAutowireCapableBeanFactory.instantiateBean()
8 // 執(zhí)行Bean的實例化方法
9 AbstractAutowireCapableBeanFactory.createBeanInstance()
10 AbstractAutowireCapableBeanFactory.doCreateBean()
11 // 執(zhí)行Bean的創(chuàng)建
12 AbstractAutowireCapableBeanFactory.createBean()
13 // 緩存中沒有,調(diào)用指定Bean工廠創(chuàng)建Bean
14 AbstractBeanFactory$1.getObject()
15 // 從單例注冊中心獲取Bean緩存
16 DefaultSingletonBeanRegistry.getSingleton()
17 AbstractBeanFactory.doGetBean()
Bean創(chuàng)建時序圖:
從調(diào)?過程可以總結(jié)出以下?點:
1. 調(diào)?BeanFact ory.get Bean() 會觸發(fā)Bean的實例化。
2. Def ault Singlet onBeanRegist ry 中緩存了單例Bean
3. Bean的創(chuàng)建與初始化是由Abst ract Aut owireCapableBeanFact ory 完成的。
3、BeanFactory 與 ApplicationContext區(qū)別
BeanFact ory 看下去可以去做IOC當(dāng)中的?部分事情,為什么還要去定義?個
Applicat ionCont ext 呢?
Applicat ionCont ext 結(jié)構(gòu)圖
從圖中可以看到 Applicat ionCont ext 它由BeanFact ory接?派??來,因?提供了
BeanFact ory所有的功能。除此之外cont ext包還提供了以下的功能:
1. MessageSource, 提供國際化的消息訪問
2. 資源訪問,如URL和?件
3. 事件傳播,實現(xiàn)了Applicat ionList ener接?的bean
4. 載?多個(有繼承關(guān)系)上下? ,使得每?個上下?都專注于?個特定的層次,?如應(yīng)?
的web層
總結(jié)回顧:
BeanDefinit ion
Def ault ResourceLoader
XmlBeanDefinit ionReader
BeanDefinit ionRegist ry
18 // 獲取Bean
19 AbstractBeanFactory.getBean()
20 // 調(diào)用的客戶類
21 com.tuling.spring.BeanFactoryExample.main()
BeanFact ory
Def ault List ableBeanFact ory
Aut owireCapableBeanFact ory
Abst ract Aut owireCapableBeanFact ory
Singlet onBeanRegist ry
Def ault Singlet onBeanRegist ry
看完上述內(nèi)容,你們掌握什么是IOC容器設(shè)計理念以及源碼怎么寫的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!