spring----06 更多DI知识
一. 延遲初始化
延遲初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用時才創(chuàng)建及初始化Bean。
配置方式很簡單只需在<bean>標(biāo)簽上指定 “lazy-init” 屬性值為“true”即可延遲初始化Bean
? ? ? ?Spring容器會在創(chuàng)建容器時提前初始化“singleton”作用域的Bean,“singleton”就是單例的意思即整個容器 每個Bean只有一個實例,后邊會詳細(xì)介紹。Spring容器預(yù)先初始化Bean通常能幫助我們提前發(fā)現(xiàn)配置錯誤,所以如果 沒有什么情況建議開啟,除非有某個Bean可能需要加載很大資源,而且很可能在整個應(yīng)用程序生命周期中很可能使用不 到,可以設(shè)置為延遲初始化。
延遲初始化的Bean通常會在第一次使用時被初始化;或者在被非延遲初始化Bean作為依賴對象注入時在會隨著初 始化該Bean時被初始化,因為在這時使用了延遲初始化Bean。
? ? ? 容器管理初始化Bean消除了編程實現(xiàn)延遲初始化,完全由容器控制,只需在需要延遲初始化的Bean定義上配置即 可,比編程方式更簡單,而且是無侵入代碼的。
二. 使用depends-on
depends-on是指指定Bean初始化及銷毀時的順序,使用depends-on屬性指定的Bean要先初始化完畢后才初始 化當(dāng)前Bean,由于只有“singleton”Bean能被Spring管理銷毀,所以當(dāng)指定的Bean都是“singleton”時,使用 depends-on屬性指定的Bean要在指定的Bean之后銷毀.
<bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> <bean id="decorator" class="cn.javass.spring.chapter3.bean.HelloApiDecorator" depends-on="helloApi"> <property name="helloApi"><ref bean="helloApi"/></property> </bean>說明:“decorator”指定了“depends-on”屬性為“helloApi”,所以在“decorator”Bean初始化之前要先初始化 “helloApi”,而在銷毀“helloApi”之前先要銷毀“decorator”。
“depends-on”屬性可以指定多個Bean,若指定多個Bean可以用“;”、“,”、空格分割。
那“depends-on”有什么好處呢?主要是給出明確的初始化及銷毀順序,比如要初始化“decorator”時要確保 “helloApi”Bean的資源準(zhǔn)備好了,否則使用“decorator”時會看不到準(zhǔn)備的資源;而在銷毀時要先在 “decorator”Bean的把對“helloApi”資源的引用釋放掉才能銷毀“helloApi”,否則可能銷毀 “helloApi”時而 “decorator”還保持著資源訪問,造成資源不能釋放或釋放錯誤。
舉例說明:
,在平常開發(fā)中我們可能需要訪問文件系統(tǒng),而文件打開、關(guān)閉是必須配對的,不能打開后不關(guān) 閉,從而造成其他程序不能訪問該文件。讓我們來看具體配置吧:
1. 準(zhǔn)備測試類:
1 public class ResourceBean { 2 private FileOutputStream fos; 3 private File file; 4 //初始化方法 5 public void init() { 6 System.out.println("ResourceBean:========初始化"); 7 //加載資源,在此只是演示 8 System.out.println("ResourceBean:========加載資源,執(zhí)行一些預(yù)操作"); 9 try { 10 this.fos = new FileOutputStream(file); 11 } catch (FileNotFoundException e) { 12 e.printStackTrace(); 13 } 14 } 15 //銷毀資源方法 16 public void destroy() { 17 System.out.println("ResourceBean:========銷毀"); 18 //釋放資源 19 System.out.println("ResourceBean:========釋放資源,執(zhí)行一些清理操作"); 20 try { 21 fos.close(); 22 } catch (IOException e) { 23 e.printStackTrace(); 24 } 25 } 26 public FileOutputStream getFos() { 27 return fos; 28 } 29 public void setFile(File file) { 30 this.file = file; 31 } 32 } View Code 1 public class DependentBean { 2 ResourceBean resourceBean; 3 public void write(String ss) throws IOException { 4 System.out.println("DependentBean:=======寫資源"); 5 resourceBean.getFos().write(ss.getBytes()); 6 } 7 //初始化方法 8 public void init() throws IOException { 9 System.out.println("DependentBean:=======初始化"); 10 resourceBean.getFos().write("DependentBean:=======初始化=====".getBytes()); 11 } 12 //銷毀方法 13 public void destroy() throws IOException { 14 System.out.println("DependentBean:=======銷毀"); 15 //在銷毀之前需要往文件中寫銷毀內(nèi)容 16 resourceBean.getFos().write("DependentBean:=======銷毀=====".getBytes()); 17 } 18 19 public void setResourceBean(ResourceBean resourceBean) { 20 this.resourceBean = resourceBean; 21 } 22 } View Code說明:
ResourceBean從配置文件中配置文件位置,然后定義初始化方法init中打開指定的文件,然后獲取文件流;最后定義銷 毀方法destroy用于在應(yīng)用程序關(guān)閉時調(diào)用該方法關(guān)閉掉文件流。
DependentBean中會注入ResourceBean,并從ResourceBean中獲取文件流寫入內(nèi)容;定義初始化方法init用來定義 一些初始化操作并向文件中輸出文件頭信息;最后定義銷毀方法用于在關(guān)閉應(yīng)用程序時想文件中輸出文件尾信息。
2. 準(zhǔn)備配置文件
<bean id="resourceBean" class="cn.javass.spring.chapter3.bean.ResourceBean" init-method="init" destroy-method="destroy"> <property name="file" value="D:/test.txt"/> </bean> <bean id="dependentBean" class="cn.javass.spring.chapter3.bean.DependentBean" init-method="init" destroy-method="destroy" depends-on="resourceBean"> <property name="resourceBean" ref="resourceBean"/> </bean>? ? ?<property name="file" value="D:/test.txt"/>配置:Spring容器能自動把字符串轉(zhuǎn)換為java.io.File。
?? ? ?init-method="init"?:指定初始化方法,在構(gòu)造器注入和setter注入完畢后執(zhí)行。? ? ?
? ? ? destroy-method="destroy":指定銷毀方法,只有“singleton”作用域能銷毀,“prototype”作用域的一定不能,其他作用域不一定能;后邊再介紹。
在此配置中,resourceBean初始化在dependentBean之前被初始化,resourceBean銷毀會在dependentBean銷 毀之后執(zhí)行。
3. 測試
1 public class MoreDependencyInjectTest { 2 @Test 3 public void testDependOn() throws IOException { 4 ClassPathXmlApplicationContext context = 5 new ClassPathXmlApplicationContext("chapter3/depends-on.xml"); 6 //一點要注冊銷毀回調(diào),否則我們定義的銷毀方法不執(zhí)行 7 context.registerShutdownHook(); 8 DependentBean dependentBean = 9 context.getBean("dependentBean", DependentBean.class); 10 dependentBean.write("aaa"); 11 } 12 } View Code測試跟其他測試完全一樣,只是在此我們一定要注冊銷毀方法回調(diào),否則銷毀方法不會執(zhí)行。
如果配置沒問題會有如下輸出:
三. 自動裝配
自動裝配就是指由Spring來自動地注入依賴對象,無需人工參與。
目前Spring3.0支持“no”、“byName ”、“byType”、“constructor”四種自動裝配,默認(rèn)是“no”指不支 持自動裝配的,其中Spring3.0已不推薦使用之前版本的“autodetect”自動裝配,推薦使用Java 5+支持的 (@Autowired)注解方式代替;如果想支持“autodetect”自動裝配,請將schema改為“spring-beans-2.5.xsd” 或去掉
自動裝配的好處是減少構(gòu)造器注入和setter注入配置,減少配置文件的長度。自動裝配通過配置<bean>標(biāo)簽的 “autowire”屬性來改變自動裝配方式。接下來讓我們挨著看下配置的含義。
表示使用默認(rèn)的自動裝配,默認(rèn)的自動裝配需要在<beans>標(biāo)簽中使用default-autowire屬性指 定,其支持“no”、“byName ”、“byType”、“constructor”四種自動裝配,如果需要覆蓋默認(rèn)自動裝配,請 繼續(xù)往下看;
1. no
意思是不支持自動裝配,必須明確指定依賴。
2. byName
通過設(shè)置Bean定義屬性autowire="byName",意思是根據(jù)名字進行自動裝配,只能用于setter注 入。比如我們有方法“setHelloApi”,則“byName”方式Spring容器將查找名字為helloApi的Bean并注入,如果找 不到指定的Bean,將什么也不注入。
<bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> <bean id="bean" class="cn.javass.spring.chapter3.bean.HelloApiDecorator" autowire="byName"/> note:這里的byName的意思是上面的bean id="helloApi"必須和HelloApiDecorator類中的依賴類HelloImpl的名字一致,private HelloImpl helloApi; 1 public class AutowireBeanTest { 2 @Test 3 public void testAutowireByName() throws IOException { 4 ClassPathXmlApplicationContext context = 5 new ClassPathXmlApplicationContext("chapter3/autowire-byName.xml"); 6 HelloApi helloApi = context.getBean("bean", HelloApi.class); 7 helloApi.sayHello(); 8 } 9 } View Code是不是不要配置<property>了,如果一個bean有很多setter注入,通過“byName”方式是不是能減少很多 <property>配置。此處注意了,在根據(jù)名字注入時,將把當(dāng)前Bean自己排除在外:比如“hello”Bean類定義了 “setHello”方法,則hello是不能注入到“setHello”的。
3. byType
通過設(shè)置Bean定義屬性autowire="byType",意思是指根據(jù)類型注入,用于setter注入,比如 如果指定自動裝配方式為“byType”,而“setHelloApi”方法需要注入HelloApi類型數(shù)據(jù),則Spring容器將查找 HelloApi類型數(shù)據(jù),如果找到一個則注入該Bean,如果找不到將什么也不注入,如果找到多個Bean將優(yōu)先注入 <bean>標(biāo)簽“primary”屬性為true的Bean,否則拋出異常來表明有個多個Bean發(fā)現(xiàn)但不知道使用哪個。讓我們用例 子來講解一下這幾種情況吧。
在根據(jù)類型注入時,將把當(dāng)前Bean自己排除在外(note:byType時,就不需要helloApi必須和依賴屬性名一樣了,只和類型有關(guān)系,因此將上面的例子中byName改成byType就可以直接運行了)
note:
通過設(shè)置Bean定義的“autowire-candidate”屬性為false來把指定Bean后自動裝配候選者中移除:
<bean class="cn.javass.spring.chapter2.helloworld.HelloImpl" autowire-candidate="false"/>4. constructor
:通過設(shè)置Bean定義屬性autowire="constructor",功能和“byType”功能一樣,根據(jù)類 型注入構(gòu)造器參數(shù),只是用于構(gòu)造器注入方式,直接將上面例子的byName改成constructor就好了
5. 全局自動裝配
可以采用在“<beans>”標(biāo)簽中通過“default-autowire”屬性指定全局的自動裝配方式,即如果defaultautowire=”byName”,將對所有Bean進行根據(jù)名字進行自動裝配。
6. 不是所有類型都能自動裝配
- 不能自動裝配的數(shù)據(jù)類型:Object、基本數(shù)據(jù)類型(Date、CharSequence、Number、URI、URL、Class、int)等;
- 通過“<beans>”標(biāo)簽default-autowire-candidates屬性指定的匹配模式,不匹配的將不能作為自動裝配的候選者,例如指定“*Service,*Dao”,將只把匹配這些模式的Bean作為候選者,而不匹配的不會作為候選者;
- 通過將“<bean>”標(biāo)簽的autowire-candidate屬性可被設(shè)為false,從而該Bean將不會作為依賴注入的候選者。
7.?數(shù)組、集合、字典類型的根據(jù)類型自動裝配和普通類型的自動裝配是有區(qū)別的:
- 數(shù)組類型、集合(Set、Collection、List)接口類型:將根據(jù)泛型獲取匹配的所有候選者并注入到數(shù)組或集合中,如“List<HelloApi> list”將選擇所有的HelloApi類型Bean并注入到list中,而對于集合的具體類型將只選擇一個候選者,“如 ArrayList<HelloApi> list”將選擇一個類型為ArrayList的Bean注入,而不是選擇所有的HelloApi類型Bean進行注入;
- 字典(Map)接口類型:同樣根據(jù)泛型信息注入,鍵必須為String類型的Bean名字,值根據(jù)泛型信息獲取,如“Map<String, HelloApi> map” 將選擇所有的HelloApi類型Bean并注入到map中,而對于具體字典類型如“HashMap<String, HelloApi> map”將只選擇類型為HashMap的Bean注入,而不是選擇所有的HelloApi類型Bean進行注入。
8. 自動裝配的優(yōu)缺點
? ? ? 優(yōu)點:首先,自動裝配確實減少了配置文件的量;其次, “byType”自動裝配能在相應(yīng)的Bean更改了字段類型時自動更新,即修改Bean類不需要修改配置,確實簡單了。
? ? ? ?缺點:最重要的缺點就是沒有了配置,在查找注入錯誤時非常麻煩,還有比如基本類型沒法完成自動裝配,所以可能經(jīng)常發(fā)生一些莫名其妙的錯誤,在此我推薦大家不要使用該方式,最好是指定明確的注入方式,或者采用最新的Java5+注解注入方式。所以大家在使用自動裝配時應(yīng)該考慮自己負(fù)責(zé)項目的復(fù)雜度來進行衡量是否選擇自動裝配方式。
?????? 自動裝配注入方式能和配置注入方式一同工作嗎?當(dāng)然可以,大家只需記住配置注入的數(shù)據(jù)會覆蓋自動裝配注入的數(shù)據(jù)。
?
9. 依賴檢查
上一節(jié)介紹的自動裝配,很可能發(fā)生沒有匹配的Bean進行自動裝配,如果此種情況發(fā)生,只有在程序運行過程中 發(fā)生了空指針異常才能發(fā)現(xiàn)錯誤,如果能提前發(fā)現(xiàn)該多好啊,這就是依賴檢查的作用。
依賴檢查:用于檢查Bean定義的屬性都注入數(shù)據(jù)了,不管是自動裝配的還是配置方式注入的都能檢查,如果沒有注入數(shù) 據(jù)將報錯,從而提前發(fā)現(xiàn)注入錯誤,只檢查具有setter方法的屬性。
Spring3+也不推薦配置方式依賴檢查了,建議采用Java5+ @Required注解方式,測試時請將XML schema降低為2.5 版本的,和自動裝配中“autodetect”配置方式的xsd一樣。
依賴檢查有none、simple、object、all四種方式,接下來讓我們詳細(xì)介紹一下:
(1)none
默認(rèn)方式,表示不檢查
(2)objects
:檢查除基本類型外的依賴對象,配置方式為:dependency-check="objects",此處我們?yōu)?HelloApiDecorator添加一個String類型屬性“message”,來測試如果有簡單數(shù)據(jù)類型的屬性為null,也不報錯;
<bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> <!-- 注意我們沒有注入helloApi,所以測試時會報錯 --> <bean id="bean" class="cn.javass.spring.chapter3.bean.HelloApiDecorator" dependency-check="objects"> <property name="message" value="Haha"/> </bean>注意由于我們沒有注入bean需要的依賴“helloApi”,所以應(yīng)該拋出異常UnsatisfiedDependencyException,表示沒有發(fā)現(xiàn)滿足的依賴:
public class DependencyCheckTest { @Test(expected = UnsatisfiedDependencyException.class) public void testDependencyCheckByObject() throws IOException { //將拋出異常 new ClassPathXmlApplicationContext("chapter3/dependency-check-object.xml"); } }(3)simple
對基本類型進行依賴檢查,包括數(shù)組類型,其他依賴不報錯;配置方式為:dependency-check="simple",以下配置中沒有注入message屬性,所以會拋出異常:
<bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> <!-- 注意我們沒有注入message屬性,所以測試時會報錯 --> <bean id="bean" class="cn.javass.spring.chapter3.bean.HelloApiDecorator" dependency-check="simple"> <property name="helloApi" ref="helloApi"/> </bean>(4)all
對所以類型進行依賴檢查,配置方式為:dependency-check="all",如下配置方式中如果兩個屬性其中一個沒配置將報錯。
<bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> <bean id="bean" class="cn.javass.spring.chapter3.bean.HelloApiDecorator" dependency-check="all"> <property name="helloApi" ref="helloApi"/> <property name="message" value="Haha"/> </bean>依賴檢查也可以通過“<beans>”標(biāo)簽中default-dependency-check屬性來指定全局依賴檢查配置。
?
參考文獻:
https://jinnianshilongnian.iteye.com/blog/1415461
?
轉(zhuǎn)載于:https://www.cnblogs.com/Hermioner/p/10193125.html
總結(jié)
以上是生活随笔為你收集整理的spring----06 更多DI知识的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【深度学习mmdetection错误】—
- 下一篇: sha256算法