SpringBean 配置方式



上篇文章介紹的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


下面是微信公眾號二維碼,歡迎來騷擾芋簿。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肪康,一起剝皮案震驚了整個濱河市撩穿,隨后出現(xiàn)的幾起案子食寡,更是在濱河造成了極大的恐慌抵皱,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伤为,居然都是意外死亡绞愚,警方通過查閱死者的電腦和手機(jī)位衩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門蚂四,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遂赠,“玉大人,你說我怎么就攤上這事筷弦±们伲” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長反症。 經(jīng)常有香客問我畔派,道長线椰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮莱衩,結(jié)果婚禮上笨蚁,老公的妹妹穿的比我還像新娘括细。我一直安慰自己奋单,他們只是感情好猫十,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布拖云。 她就那樣靜靜地躺著宙项,像睡著了一般乏苦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天汇荐,我揣著相機(jī)與錄音洞就,去河邊找鬼。 笑死掀淘,一個胖子當(dāng)著我的面吹牛旬蟋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播革娄,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼稠腊!你這毒婦竟也來了躁染?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤架忌,失蹤者是張志新(化名)和其女友劉穎吞彤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叹放,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡饰恕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了井仰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片埋嵌。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖俱恶,靈堂內(nèi)的尸體忽然破棺而出雹嗦,到底是詐尸還是另有隱情,我是刑警寧澤合是,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布了罪,位于F島的核電站,受9級特大地震影響聪全,放射性物質(zhì)發(fā)生泄漏泊藕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一难礼、第九天 我趴在偏房一處隱蔽的房頂上張望娃圆。 院中可真熱鬧,春花似錦蛾茉、人聲如沸讼呢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吝岭。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窜管,已是汗流浹背散劫。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留幕帆,地道東北人获搏。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像失乾,于是被迫代替她去往敵國和親常熙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容