上篇文章介紹的IOC的理念與優(yōu)點憨募,這一章我們介紹如何將自己開發(fā)的Bean裝配到Spring IOC容器中区赵。
Spring 配置3種方式
在XML文件中顯式配置
在Java 接口實現(xiàn)配置
隱式Bean 的發(fā)現(xiàn)機(jī)制和自動裝配原則
Spring 如何選擇配置浪南?
這方面络凿,并沒有唯一的正確答案。你所做出的選擇必須要適合你和你的項目絮记。而且怨愤,誰說我們只能選擇其中的一種方案呢?Spring的配置風(fēng)格是可以互相搭配的篮愉,所以你可以選擇使用XML裝配一些bean潜支,使用Spring基于Java的配置(JavaConfig)來裝配另一些bean,而將剩余的 bean讓Spring去自動發(fā)現(xiàn)埠对。?
裝配方式的優(yōu)先級
在現(xiàn)實的工作中裁替,這 3 種方式都會被用到弱判,并且在學(xué)習(xí)和工作之中常常混合使用开伏,所以這里給出一些關(guān)于這 3 種優(yōu)先級的建議:隱式Bean的發(fā)現(xiàn)機(jī)制和自動裝配原則
首選自動裝配機(jī)制固灵,顯示配置越少越好劫流。
優(yōu)點:減少程序開發(fā)者的決定權(quán),簡單又不失靈活仍秤。
Java 的接口和類中實現(xiàn)配置
在沒有辦法使用自動裝配原則情況诗力,我們考慮此類方法根穷。
優(yōu)點:減少xml配置文件屿良,也更為容易惫周。
xml文件配置
最后選擇xml文件配置递递。
優(yōu)點:簡單易懂
典型場景:
當(dāng)使用三方類時啥么,并不是我們開發(fā)悬荣,也無法修改代碼疙剑,這個時候通過XML方式配置使用言缤。
自動化裝配Bean
從兩個角度來實現(xiàn)自動化裝配
組件掃描(component scaning)? ?
自動裝配(autowing)
組件掃描
Spring 會自動發(fā)現(xiàn)應(yīng)用上下文所創(chuàng)建的Bean管挟。
自動裝配(autowing)
Spring 自動滿足Bean之間的依賴。組件掃描和自動裝配組合在一起就能發(fā)揮出強(qiáng)大的威力导帝,它們能夠?qū)⒛愕娘@式配置降低到最少舟扎。
?代碼實現(xiàn)
在這個MP3和流式媒體音樂的時代悴务,磁帶顯得陳舊,隨著以物理載體方式越來越少羡疗。盡管如此叨恨,磁帶為我們闡述DI如何運行提供一個很好的樣例挖垛。如果不將磁帶放入磁帶播放器中痢毒,那么磁帶其實沒有太大用處,所有可以說磁帶依賴于磁帶播放器栋荸。
磁帶接口?
public?interface?Tape?{
????void?play();
}
熊貓播放器實現(xiàn)類
@Component
public?class?PandaPlayer?implements?Tape?{
private?String?singer?=?"周杰倫";
private?String?songName?=?"雙截棍";
public?void?play()?{
????System.out.println("開始播放:"+singer+"的"+songName);
}
}
配置類
@Configuration
@ComponentScan
public?class?TapeConfig?{
}
測試類
@RunWith(SpringJUnit4ClassRunner.class)
?//?自動裝配的配置
?@ContextConfiguration(classes?=?TapeConfig.class)
?public?class?PlayerTest?{
//?1晌块、通過掃描包的方式實現(xiàn)裝配bean匆背,使用@Component注解或@Named?注解進(jìn)行配置
@Autowired?
private?Tape?tape;
@Test?
public?void?player(){?
????assertNotNull(tape);?tape.play();
}
}
測試結(jié)果
開始播放:周杰倫的雙截棍
如果沒有其他配置的話钝尸,@ComponentScan默認(rèn)會掃描與配置類相同的包。因為TapeConfig類位于com.automatic.service包中奶浦,因此Spring 將會掃描這個包以及這個包下的所有子包踢星,查找?guī)в蠤Component注解的類沐悦。這樣的話藏否,就能發(fā)現(xiàn)Tape,并且會在Spring中自動為 其創(chuàng)建一個bean遥椿。
自動裝配
簡單來說冠场,自動裝配就是讓Spring自動滿足bean依賴的一種方法本砰,在滿足依賴的過程中点额,會在Spring應(yīng)用上下文中尋找匹配某個bean需求的其他bean。為了聲明要進(jìn)行自動裝配载慈,我們可以借助Spring的@Autowired注解娃肿。
使用@Autowired是放到構(gòu)造方法中珠十,@Autowired注解不僅能夠用在構(gòu)造器上焙蹭,還能用在屬性的Setter方法上或者普通方法中。?
Player播放接口
public?interface?Player?{
void?play();
}
索尼播放器實現(xiàn)類
@Component
public?class?SonyPlayer?implements?Player?{
private?Tape?tape;
@Autowired
public?SonyPlayer?(Tape?tape)?{
????this.tape?=?tape;
}
public?void?play()?{
????System.out.println("----start----");
????tape.play();
????System.out.println("----end----");
}
}
測試方法
@Autowired
?private?Player?player;
?@Test?public?void?player(){
??????assertNotNull(player);?
??????player.play();?
}??
測試結(jié)果
?----start----
?開始播放:周杰倫的雙截棍
?----end----
?
通過Java代碼裝配Bean
盡管在很多場景下通過組件掃描和自動裝配實現(xiàn)Spring的自動化配置是更為推薦的方式,但有時候自動化配置的方案行不通粪般,因此需要明確配置Spring污桦。比如說凡橱,你想要將第三方庫中的組件裝配到你的應(yīng)用中,在這種情況下顾稀,是沒有辦法在它的類上添加@Component和@Autowired注解的静秆,因此就不能使用自動化裝配的方案了巡李。
下面的例子是參考《Sring實戰(zhàn)第四版》击儡,覺得比較容易理解:
CD播放類
public?class?CDPlayer?{
private?CompactDisc?cd;
public?void?play()?{
????cd.play();
}
//?省略setget方法
}
播放接口
public?interface?CompactDisc?{
void?play();
}
歌曲
?public?class?HardDaysNight??implements?CompactDisc?{
private?String?title?=?"Hard.?DaysNight's?Lonely?Hearts?Club?Band";
private?String?artist?=?"The?Hard";
public?void?play()?{
????System.out.println("Playing?"?+?title?+?"?by?"?+?artist);
}
}
public?class?Revolver?implements?CompactDisc?{
private?String?title?=?"Revolver.?Revolver's?Lonely?Hearts?Club?Band";
private?String?artist?=?"The?Revolver";
public?void?play()?{
????System.out.println("Playing?"?+?title?+?"?by?"?+?artist);
}
}
public?class?SgtPeppers?implements?CompactDisc?{
private?String?title?=?"Sgt.?Pepper's?Lonely?Hearts?Club?Band";
private?String?artist?=?"The?Beatles";
public?void?play()?{
????System.out.println("Playing?"?+?title?+?"?by?"?+?artist);
}
}
public?class?WhiteAlbum??implements?CompactDisc?{
private?String?title?=?"White.?Album's?Lonely?Hearts?Club?Band";
private?String?artist?=?"The?White";
public?void?play()?{
????System.out.println("Playing?"?+?title?+?"?by?"?+?artist);
}
}
重點在這里Config配置類
?/*
?*?Spring?的組件掃描默認(rèn)是不啟用的蛀柴,需要顯式配置啟用組件掃描去尋找被?
?@Component 注解修飾的組件類矫夯,并為其創(chuàng)建 bean 實例训貌。
?*?*/
?/*
??*?標(biāo)記類?CDPlayerConfig?是?Spring?的配置類冒窍,通過?java?代碼定義?
? Spring 的裝配規(guī)則综液。
??*?該類應(yīng)該包含在Spring應(yīng)用上下文中如何創(chuàng)建bean的細(xì)節(jié)谬莹。
??*?*/
@Configuration
public?class?CDPlayerConfig?{
????/*
????*?要在 JavaConfig 中聲明 bean 桩了,我們需要編寫一個方法井誉,這個方法會創(chuàng)建所需類型的實例,然后給這個方法添加?@Bean 注解喳钟。
????*
????*?@Bean 注解會告訴 Spring 這個方法將會返回一個對象荚藻,該對象要注冊為 Spring 應(yīng)用上下文中的 bean 应狱。
????*?方法體中包含了最終產(chǎn)生 bean 實例的邏輯祠丝。
????*
????*?默認(rèn)写半,bean 的 ID 和方法名是一樣的,如下 bean 的 ID 為 sgtPeppers 璃岳。
????*?如果想手動為 bean 指定一個 ID 铃慷,可以使用?@Bean 的 name 屬性蜕该,如:
????*???@Bean(name="lonelyHeartsClubBand")
????*?*/
????@Bean
????public?CompactDisc?sgtPeppers()?{
????????return?new?SgtPeppers();
????}
????//?這里是使用 Java 創(chuàng)建 bean堂淡,因此我們可以發(fā)揮 Java 提供的所有功能,只要最終生成一個 CompactDisc 實例即可瘾腰。例如:
????@Bean
????public?CompactDisc?randomBeatlesCD()?{
????????CompactDisc?cd;
????????int?choice?=?(int)?Math.floor(Math.random()?*?4);
????????switch?(choice)?{
????????????case?1:
????????????????cd?=?new?SgtPeppers();
????????????????break;
????????????case?2:
????????????????cd?=?new?WhiteAlbum();
????????????????break;
????????????case?3:
????????????????cd?=?new?HardDaysNight();
????????????????break;
????????????default:
????????????????cd?=?new?Revolver();
????????????????break;
????????}
????????return?cd;
????}
????/*
????* Spring 裝配方式一:
????*?在JavaConfig中裝配bean的最簡單方式就是引用創(chuàng)建bean的方法居灯。
????*?*/
????@Bean
????public?CDPlayer?cdPlayer()?{
??//????????????return?new?CDPlayer(sgtPeppers());
????????return?new?CDPlayer(new?WhiteAlbum());
????}
????/*
????* Spring 裝配方式二:
????*?當(dāng) Spring 調(diào)用 cdPlayer()?創(chuàng)建 CDPlayer bean 的時候,它會自動裝配一個 CompactDisc 到配置方法之中义锥。
????*?然后拌倍,方法體就可以按照合適的方式來使用它柱恤。
????*
????*?通過這種方式引用其他的 bean 通常是最佳的選擇,因為它不會要求將 CompactDisc 聲明到同一個配置類之中泡孩。
????*?在這里甚至沒有要求?CompactDisc?必須要在?JavaConfig?中聲明仑鸥,
????*?實際上它可以通過組件掃描功能自動發(fā)現(xiàn)或者通過 XML 來進(jìn)行配置变屁。
????*?你可以將配置分散到多個配置類粟关、XML?文件以及自動掃描和裝配?bean?之中闷板,
????*?只要功能完整健全即可。不管 CompactDisc 是采用什么方式創(chuàng)建出來的击孩,
????* Spring 都會將其傳入到配置方法中巩梢,并用來創(chuàng)建 CDPlayer bean 括蝠。
????*
????*?當(dāng)配置類中有多個同類型的 bean 時,此時可以使用?@Qualifier 注解來指定參數(shù)注入的是哪一個具體的 bean 搁拙。
????*?*/
????@Bean
????public?CDPlayer?cdPlayer(@Qualifier("randomBeatlesCD")?CompactDisc?compactDisc)?{
????????return?new?CDPlayer(compactDisc);
????}
????/*
????*?我們也可以采用屬性賦值的方式來注入依賴值箕速,這里所存在的可能性僅僅受到Java語言的限制盐茎。
????*?*/
??//????????@Bean
//????????public?CDPlayer?cdPlayer(@Qualifier("sgtPeppers")?
??CompactDisc?compactDisc)?{
??//????????????CDPlayer?cdPlayer?=?new?CDPlayer();
?//????????????cdPlayer.setCd(compactDisc);
?//????????????return?cdPlayer;
??//????????}
???}
測試方法
?@Autowired
?private?CDPlayer?cdPlayer;
?@Test
?public?void?cdShouldNotBeNull()?{
????assertNotNull(cdPlayer);
????cdPlayer.play();
?}
測試結(jié)果
?Playing?Revolver.?Revolver's?Lonely?Hearts?Club?Band?by?The?Revolver
每次運行的結(jié)果是不同字柠,因為randomBeatlesCD()方法窑业。
XML文件裝配Bean
使用xml文件進(jìn)行配置枕屉,配置比較簡單易懂搀庶。
public?class?ComplexAssembly?{
private?Long?id;
private?List<String>?list;
private?Map<String,?String>?map;
private?Properties?properties;
private?Set<String>?set;
private?String[]?array;
//?省略setget方法
}
這個 Bean 沒有任何的實際意義哥倔,只是為了介紹如何裝配這些常用的集合類:
xml文件
<bean?id="complexAssembly"?class="com.xml.service.ComplexAssembly">
<!--?裝配Long類型的id?-->
<property?name="id"?value="1"/>
<!--?裝配List類型的list?-->
<property?name="list">
????<list>
????????<value>value-list-1</value>
????????<value>value-list-2</value>
????????<value>value-list-3</value>
????</list>
</property>
<!--?裝配Map類型的map?-->
<property?name="map">
????<map>
????????<entry?key="key1"?value="value-key-1"/>
????????<entry?key="key2"?value="value-key-2"/>
????????<entry?key="key3"?value="value-key-2"/>
????</map>
</property>
<!--?裝配Properties類型的properties?-->
<property?name="properties">
????<props>
????????<prop?key="prop1">value-prop-1</prop>
????????<prop?key="prop2">value-prop-2</prop>
????????<prop?key="prop3">value-prop-3</prop>
????</props>
</property>
<!--?裝配Set類型的set?-->
<property?name="set">
????<set>
????????<value>value-set-1</value>
????????<value>value-set-2</value>
????????<value>value-set-3</value>
????</set>
</property>
<!--?裝配String[]類型的array?-->
<property?name="array">
????<array>
????????<value>value-array-1</value>
????????<value>value-array-2</value>
????????<value>value-array-3</value>
????</array>
</property>
</bean>
測試方法
?????@Test
?public?void?getXmlComplexAssembly()?{
?????ApplicationContext?context?=?new?ClassPathXmlApplicationContext(
????????????????new?String[]{"applicationContext.xml"}
????????????????);
?????ComplexAssembly?complexAssembly?=?(ComplexAssembly)?context.getBean("complexAssembly");
?????System.out.println(complexAssembly.getId());
?????System.out.println(complexAssembly.getMap().get("key1"));
?}
測試結(jié)果
??1
??value-key-1
總結(jié):
??????List?屬性為對應(yīng)的?<list>?元素進(jìn)行裝配东抹,然后通過多個?<value>?元素設(shè)值
??????Map?屬性為對應(yīng)的?<map>?元素進(jìn)行裝配缭黔,然后通過多個?<entry>?元素設(shè)值蒂破,?只是?entry?包含一個鍵值對(key-value)的設(shè)置
??????Properties?屬性為對應(yīng)的?<properties>?元素進(jìn)行裝配附迷,通過多個?<property>?元素設(shè)值,只是?properties?元素有一個必填屬性?key?拨与,然后可以設(shè)置值
??????Set?屬性為對應(yīng)的?<set>?元素進(jìn)行裝配艾猜,然后通過多個?<value>?元素設(shè)值
?對于數(shù)組而言匆赃,可以使用?<array>?設(shè)置值炸庞,然后通過多個?<value>?元素設(shè)值。
命名空間裝配
除了上述的配置之外, Spring 還提供了對應(yīng)的命名空間的定義滥壕,只是在使用命名空間的時候要先引入對應(yīng)的命名空間和 XML 模式(XSD)文件绎橘。
p-命名空間
c-命名空間
-
util-命名空間
p標(biāo)簽
使用p命名空間需要添加
<!--?使用p標(biāo)簽前?-->????????????????????????
?<bean?id="complexAssembly"?
?class="com.xml.service.ComplexAssembly">
????<property?name="id"?value="1"/>
?</bean>
<!--?使用p標(biāo)簽后?-->
<bean?name="complexassembly2"?class="com.xml.service.ComplexAssembly"??p:id="2"?/>
c標(biāo)簽
通過這個類舉例子
?package?pojo;
?public?class?Student?{?
???int?id;?
???String?name;
??public?Student(int?id,?String?name)?
??{?
???this.id?=?id;?this.name?=?name;?
??}?
???//?setter?and?getter?
}
?在 c-命名空間和模式聲明之后,我們就可以使用它來聲明構(gòu)造器參數(shù)了:
?<!--?引入?c-命名空間之前?-->
?<bean?name="student1"?class="pojo.Student">
???<constructor-arg?name="id"?value="1"?/>
????<constructor-arg?name="name"?value="學(xué)生1"/>
</bean>
?<!--?引入?c-命名空間之后?-->
<bean?name="student2"?class="pojo.Student"
???????c:id="2"?c:name="學(xué)生2"/>
util標(biāo)簽
<!--?引入util-命名空間之前?-->
<property?name="list">
<list>
????<ref?bean="bean1"/>
????<ref?bean="bean2"/>
</list>
</property>
<!--?引入util-命名空間之后?-->
<util:list?id="list">
???<ref?bean="bean1"/>
???<ref?bean="bean2"/>
?</util:list>
<util:list>
?只是 util-命名空間中的多個元素之一,下表提供了 util-命名空間提供的所有元素:
Spring 作用域
總??結(jié)
在Spring中裝配bean的三種主要方式:自動化配置、基于Java的顯式配置以及基于XML的顯式配置慌盯。不管你采用什么方式亚皂,這些技術(shù)都描述了Spring應(yīng)用中的組件以及這些組件之間的關(guān)系灭必。?
本文中源碼:
https://github.com/xiaonongOne/spring-bean
下面是微信公眾號二維碼,歡迎來騷擾芋簿。