聲明:后面的章節(jié)是看了《Spring
實(shí)戰(zhàn)》所做的筆記,相關(guān)內(nèi)容也是摘抄下來翁垂,這里只是自己做個記錄。
一硝桩、Spring配置的可選方案
Spring
提供了三種主要的裝配機(jī)制:
- 在
XML
中進(jìn)行顯示配置 - 在
Java
中進(jìn)行顯示配置 - 隱式的
bean
發(fā)現(xiàn)機(jī)制和自動裝配
建議是盡可能地使用自動配置的機(jī)制沿猜,顯示配置越少越好。當(dāng)必須要顯示配置的時候碗脊,推薦使用類型安全并且比XML
更加強(qiáng)大的JavaConfig
啼肩。只有當(dāng)想要使用便利的XML
命名空間,并且在JavaConfig
中沒有同樣的實(shí)現(xiàn)時衙伶,才應(yīng)該使用XML
祈坠。
二、自動化裝配bean
Spring
從兩個角度來實(shí)現(xiàn)自動化裝配:
- 組件掃描:
Spring
會自動發(fā)現(xiàn)應(yīng)用上下文中所創(chuàng)建的bean
- 自動裝配:
Sping
自動滿足bean
之間的依賴
2.1 創(chuàng)建可被發(fā)現(xiàn)的bean
下面使用例子說明:
CompactDisc.java
package soundsystem;
//這是一個CD接口矢劲,表示CD
public interface CompactDisc {
void play();
}
SgPeppers.java
package soundsystem;
import org.springframework.stereotype.Component;
//這是CD接口的一個實(shí)現(xiàn)類赦拘,其中包含CD名字和藝術(shù)家的名字
@Component
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);
}
}
說明:@Component
表示這個類是一個組件類,在裝配過程中要將其創(chuàng)建為一個bean
芬沉。下面配置自動掃面:
CDPlayerConfig
package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
說明:這里通過這個類定義了Spring
裝配的規(guī)則躺同,其中@Configuration
表明這是一個裝配規(guī)則,而雖然這里沒有顯示的聲明要裝配哪些bean
丸逸,但是使用@ComponentScan
就表示默認(rèn)掃面本包中的所有類蹋艺,如果發(fā)現(xiàn)某個類中配置了@Component
注解,那么就將那些類裝配為bean
黄刚。當(dāng)然也可以顯示注明掃面哪個包捎谨,下面先看使用XML
的方式,之后會說明使用Java
的方式:
<context:component-scan base-package="soundsystem" />
說明:<context:component-scan>
元素還有一些屬性和子元素,這里不細(xì)說涛救,加入我們將之前的@ComponentScan
去掉畏邢,而是使用上面的XML
配置,那如何才能讓CDPlayerConfig
知道呢州叠?這需要使用后面要講到的@Import
注解棵红。下面看一個測試:
package soundsystem;
import static org.junit.Assert.*;
import ......
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
@Autowired
private CompactDisc cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);//斷言cd不為null
}
}
說明:這里@RunWith(SpringJUnit4ClassRunner.class)
表明讓Spring
自動創(chuàng)建上下文。而@ContextConfiguration(classes=CDPlayerConfig.class)
表明告訴此類要在CDPlayerConfig
類中加載相關(guān)的配置咧栗,@Autowired
表示自動注入實(shí)現(xiàn)了CompactDisc
接口的實(shí)例逆甜。
2.1.1 為組件掃面的bean命名
在創(chuàng)建一個bean
時,默認(rèn)使用組件類的類名為ID
(但是將首字母小寫)致板,但是我們也可以自己顯示定義ID
:
@Component("longelyHeartsClub")
public class SgtPeppers implements CompactDisc {
......
}
說明:此時交煞,這個類在被創(chuàng)建為bean
的時候的ID
就為longelyHeartsClub
。
2.1.2 設(shè)置組件掃描的基礎(chǔ)包
之前我們沒有為@ComponentScan
配置任何參數(shù)斟或,于是其默認(rèn)掃描的是類的包素征,同時也可以使用XML
方式顯示的指明要掃描的包,下面我們?yōu)槠渑渲孟嚓P(guān)的屬性:
@Configuration
@ComponentScan("soundsystem")
public class CSPlayerConfig(){}
說明:這里就是配置了一個自動掃面的基礎(chǔ)包萝挤,當(dāng)然我們可以更清晰的指明這是一個掃描基礎(chǔ)包:
@ComponentScan(basePackeges="soundsystem")
說明:當(dāng)然這里也可以同時指定多個掃描的基礎(chǔ)包:
@ComponentScan(basePackeges={"soundsystem","vedio"})
說明:但是這里使用字符串的方式不夠安全御毅,我們推薦類的方式,就是將其指定為包中所含類或接口:
@ComponentScan(basePackegeClasses={CDPlayer.class, DVDPlayer.class})
說明:此時在掃描的時候就會掃描這兩個類或接口所在包的所有類和接口怜珍。即這些類或接口所在的包將被指定為掃描基礎(chǔ)包端蛆。我們還可以考慮在包中創(chuàng)建一個用來進(jìn)行掃描的空標(biāo)記接口,這樣不會影響業(yè)務(wù)類今后的重構(gòu)工作酥泛。
2.2.3 通過為bean添加注解實(shí)現(xiàn)自動裝配
有些組件類在被創(chuàng)建為bean
的過程中可能依賴其他的bean
今豆,可以通過注解的方式讓Spring
自動幫我們注入進(jìn)來:
package soundsystem;
public interface MediaPlayer {
void play();
}
package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
說明:這里CDPlayer
類在被創(chuàng)建為bean
時依賴一個CompactDisc
的實(shí)現(xiàn)類,于是使用@Autowired
將這個bean
注入進(jìn)來柔袁。如果沒有匹配的bean
或者有多個匹配的bean
呆躲,將會產(chǎn)生異常。
三捶索、通過Java代碼裝配bean
前面說的是一種自動裝配和自動注入的方式插掂,但是有時候是沒辦法使用這種自動方式的,比如向?qū)⒌谌綆熘械慕M件裝配到你的應(yīng)用中腥例。此時必須使用顯示的方式燥筷,即Java
和XML
配置方式。而對于Java
方式配置bean
則可以直接在CDPlayerConfig.java
中配置院崇,而對于XML
方式的bean
肆氓,可以使用@Import
引用。
3.1 創(chuàng)建配置類
首先修改之前的配置類底瓣,讓其不要自動掃描裝配了:
package soundsystem;
@Configuration
public class CDPlayerConfig {
}
說明:此時沒有配置@ComponentScan
則就不會自動創(chuàng)建相關(guān)的bean
了谢揪。
3.2 聲明簡單的bean
要在JavaConfig
中聲明bean
蕉陋,我們需要編寫一個方法,這個方法創(chuàng)建所需類型的實(shí)例拨扶,然后給這個方法添加@Bean
注解:
@Bean
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
說明:這里注解會告訴Spring
這個方法會返回一個對象凳鬓,該對象要注冊為Spring
易用上下文中的bean
。默認(rèn)的ID
和帶有@Bean
注解的方法名一樣患民,當(dāng)然也可以自己指定:
@Bean(name="lonelyHeartsClubBand")
3.3 借助JavaConfig實(shí)現(xiàn)注入
有些bean
的創(chuàng)建可能依賴于其他bean
的創(chuàng)建缩举,我們需要將多個bean
裝配在一起,在JavaConfig
中裝配bean
的最簡單的方式就是引用創(chuàng)建bean
的方法匹颤,如:
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(sgtPeppers());
}
說明:這里可以看到CDPlayer bean
的創(chuàng)建依賴于CompactDisc bean
仅孩,于是我們調(diào)用了能夠產(chǎn)生CompactDisc bean
的方法。但是這里注意印蓖,看起來辽慕,CompactDisc
是通過sgtPeppers()
得到的,但是并不是如此赦肃,因?yàn)檫@個方法上添加了@Bean
注解溅蛉,Spring
會攔截所有對它的實(shí)際調(diào)用,確保直接返回該方法所創(chuàng)建的bean
他宛,比如此時還有另一個bean
的創(chuàng)建依賴CompactDisc bean
:
@Bean
public CDPlayer anotherCDPlayer() {
return new CDPlayer(sgtPeppers());
}
說明:如果每次創(chuàng)建bean
都是實(shí)際調(diào)用sgtPeppers()
方法船侧,那么每個bean
都擁有自己特有的CompactDisc bean
厅各,但其實(shí)不是镜撩,默認(rèn)情況下讯检,Spring
中的bean
都是單例的顾翼。當(dāng)然還有一種更為簡單的方式:
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
return new CDPlayer(compactDisc);
}
說明:這種方式可能更容易理解,這里不用明確引用CompactDisc
的@Bean
方法拜姿,通過這種方式引用其他的bean
通常是最佳的選擇烙样。此時,不管CompactDisc
是采用什么方式創(chuàng)建出來的蕊肥,Spring
都會將其傳入到配置方法中谒获,并用來創(chuàng)建CDPlayer bean
。這里我們使用的是CDPlayer
的構(gòu)造器實(shí)現(xiàn)了DI
功能,但是還有其他方式可以實(shí)現(xiàn)批狱,比如通過Setter
方式:
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
CDPlayer cdPlayer = new CDPlayer(compactDisc);
cdPlayer.setCompactDisc(compactDisc);//這里實(shí)現(xiàn)了DI功能
return cdPlayer;
}
說明:我們可以采用任何必要的Java
功能來產(chǎn)生bean
實(shí)例裸准,僅僅收到Java
語言的限制。
四赔硫、通過XML裝配bean
4.1 聲明一個簡單的<bean>
這里我們需要添加一個<bean>
元素炒俱,其類似于JavaConfig
中的@Bean
:
<bean class="soundsystem.SgtPeppers" />
說明:這里聲明了一個簡單的bean
,創(chuàng)建這個bean
的類通過class
屬性來指定爪膊,并且要使用全限定的類型权悟。而這里沒有明確指定ID
,所以這個bean
將會根據(jù)全限定類名來進(jìn)行命名惊完,這里即為"soundsystem.SgtPeppers#0"
僵芹,其中"#0"
是一個計(jì)數(shù)的形式,用來區(qū)分相同類型的其他bean
小槐,如果另外聲明一個SgtPeppers
拇派,并且沒有明確指定ID
,那么其ID
即為"soundsystem.SgtPeppers#1"
凿跳。
4.2 借助構(gòu)造器注入初始化bean
在XML
中聲明DI
時件豌,會有多種可選的配置方案和風(fēng)格,具體到構(gòu)造器注入控嗜,有兩種基本的配置方案:
-
<constructor-age>
元素 - 使用
spring3.0
所引入的c-
命名空間
4.2.1 構(gòu)造器注入bean引用
之前我們已經(jīng)聲明了一個SgtPeppers
的bean
茧彤,并且這個類實(shí)現(xiàn)了CompactDisc
接口,所以實(shí)際上我們已經(jīng)有了一個可以注入到CDPlayer bean
中的bean
疆栏,所以現(xiàn)在要做的就是通過DI
引用SgtPeppers
:
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-age ref="compactDisc"/>
</bean>
說明:spring
會創(chuàng)建一個CDPlayer
實(shí)例曾掂,同時 <constructor-age>
會告知spring
要將一個ID
為compactDisc
的bean
引用傳遞到CDPlayer
的構(gòu)造器中。
當(dāng)然作為替代方案壁顶,可以使用c-
命名空間珠洗,只是需要在XML
配置的頂部聲明其模式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
......
</beans>
說明:于是可以將之前的配置方法改為如下:
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc" />
說明:屬性名以"c-"
開頭,也就是命名空間的前綴若专。接下來就是要裝配的構(gòu)造器參數(shù)名许蓖,在此之后是"-ref"
,它會告訴spring
调衰,正在裝配的是一個bean
的引用膊爪,這個bean
名字是comapactDisc
,而不是字面量"comapactDisc"
嚎莉。
注意:這里的"cd"
是構(gòu)造器參數(shù)名米酬,但是直接使用參數(shù)名可能不太好,我們也可以這樣:
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_0-ref="compactDisc" />
說明:這里使用數(shù)字表示構(gòu)造器參數(shù)的位置趋箩,也就是第幾個參數(shù)淮逻,由于在XML
中不允許數(shù)字作為屬性的第一個字符琼懊,所以在前面加了一個下劃線。當(dāng)然如果只有一個構(gòu)造器參數(shù)爬早,可以將數(shù)字拿掉也可以哼丈。
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_-ref="compactDisc" />
4.2.2 將字面量注入到構(gòu)造器中
這里先給出一個CompactDisc
的新實(shí)例:
package soundsystem.properties;
import soundsystem.CompactDisc;
public class BlankDisc implements CompactDisc {
private String title;
private String artist;
public BlankDisc (String title,String artist) {
this.artist = artist;
this.title = title;
}
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
說明:在之前的SgtPeppers
類中,唱片名稱和藝術(shù)家的名字都是硬編碼的筛严,這里我們讓其更加靈活:
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt.Peppers's Lonely Hearts Club Band"/>
<constructor-arg value="The Beatles"/>
</bean>
說明:這里使用value
屬性醉旦,就是表示給定的值要以字面量的行駛證注入到構(gòu)造器中。如果要使用"c-"
命名空間桨啃,則配置如下:
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:_title="Sgt.Peppers's Lonely Hearts Club Band"
c:_artist="The Beatles"/>
當(dāng)然亦可以這樣:
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:_0="Sgt.Peppers's Lonely Hearts Club Band"
c:_1="The Beatles"/>
4.2.3 裝配集合
這里先將上面的BlankDisc
類改動:
package soundsystem.properties;
import java.util.List;
import soundsystem.CompactDisc;
public class BlankDisc implements CompactDisc {
private String title;
private String artist;
private List<String> tracks;
public BlankDisc (String title,String artist,List<String> tracks) {
this.artist = artist;
this.title = title;
this.tracks= tracks;
}
public void play() {
System.out.println("Playing " + title + " by " + artist);
for (String track : tracks) {
System.out.println("-Track: " + track);
}
}
}
說明:這里增加了一個磁道集合屬性车胡,這個屬性在配置時必須配置,如果沒有具體的值傳遞照瘾,可以配置為null
匈棘,但是這在調(diào)用play()
方法時會拋出空指針異常,于是我們需要配置一個List
列表:
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt.Peppers's Lonely Hearts Club Band"/>
<constructor-arg value="The Beatles"/>
<constructor-arg>
<list>
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little...</value>
<value>Getting Better</value>
...
</list>
</constructor-arg>
</bean>
說明:當(dāng)然集合列表也可以配置為bean
引用:
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt.Peppers's Lonely Hearts Club Band"/>
<constructor-arg value="The Beatles"/>
<constructor-arg>
<list>
<ref bean="sgtPeppers"/>
<ref bean="whiteAlbum"/>
<ref bean="revolver"/>
...
</list>
</constructor-arg>
</bean>
說明:也可以使用Set
集合析命。而目前使用c-
命名空間的屬性無法實(shí)現(xiàn)裝配集合的功能主卫。
4.3 設(shè)置屬性
在之前的注入中都是使用構(gòu)造器注入的,沒有使用Setter
方法鹃愤,這里看看如何使用XML
配置實(shí)現(xiàn)屬性注入:
package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
說明:可能我們會覺得即使沒有將CompactDisc
裝入進(jìn)來簇搅,CDPlayer
依然還能具備一些有限的功能,但是在測試相關(guān)功能時可能會出現(xiàn)空指針異常软吐,使用XML
配置實(shí)現(xiàn)屬性配置的方式如下:
<bean id="cdPlayer" class="soundsystem.cdPlayer">
<property name="compactDisc" ref="compactDisc"/>
</bean>
說明:<property>
元素為屬性的Setter
方法所提供的功能與<constructor-arg>
元素為構(gòu)造器提供的功能是一樣的瘩将。使用命名空間的方式為:
<bean id="cdPlayer" class="soundsystem.cdPlayer"
p:compactDisc-ref="compactDisc"/>
說明:當(dāng)然和之前一樣,也要在配置文件頭部加上:
xmlns:p="http://www.springframework.org/schema/p"
相關(guān)內(nèi)容和之前的c-
命名空間類似凹耙。
4.3.1 將字面量注入到屬性中
相關(guān)配置基本上和c-
命名空間一致姿现,這里不再細(xì)說。雖然也不能使用p-
命名空間來裝配集合肖抱,但是可以使用spring util-
命名空間中的一些功能類簡化BlankDisc bean
备典,首先在配置文件頭部加上:
xmlns:util="http://www.springframework.org/schema/util"
說明:util-
命名空間所提供的功能之一就是<util:list>
元素,它會創(chuàng)建一個列表bean
虐沥。如下:
<util:list id="trackList">
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little...</value>
<value>Getting Better</value>
...
</util:list>
于是我們就可以使用p-
命名空間簡化屬性配置了:
<bean id="compactDisc" class="soundsystem.BlankDisc"
p:title="Sgt.Peppers's Lonely Hearts Club Band"
p:artist="The Beatles"
p:tracks-ref="trackList"/>
說明:util-
命名空間中還有很多其他元素:
元素 | 描述 |
---|---|
<util:constant> |
引用某個類型的public static 域熊经,并將其暴露為bean
|
<util:list> |
創(chuàng)建一個java.util.List 類型的bean 泽艘,其中包含值或引用 |
<util:map> |
創(chuàng)建一個java.util.Map 類型的bean 欲险,其中包含值或引用 |
<util:properties> |
創(chuàng)建一個java.util.Properties 類型的bean
|
<util:property-path> |
引用一個bean 屬性(或內(nèi)嵌屬性),并將其暴露為bean
|
<util:set> |
創(chuàng)建一個java.util.Set 類型的bean 匹涮,其中包含值或引用 |
五天试、導(dǎo)入和混合配置
5.1 在JavaConfig中引用XML配置
現(xiàn)在假設(shè)CDPlayerConfig
已經(jīng)變得很復(fù)雜,需要將其拆分為多個配置:
package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CDConfig {
@Bean
public CompactDisc compactDisc(){
return new SgtPeppers();
}
}
說明:此時compactDisc()
方法已經(jīng)從CDPlayerConfig
中移除掉了然低,這里需要將兩個配置組合在一起:
package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(CDConfig.class)
public class CDPlayerConfig {
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(compactDisc());
}
}
這樣便使用@Import
將兩個配置組合在了一起喜每,當(dāng)然更好的方法是創(chuàng)建一個更高級別的SoundSystemConfig
务唐,在其中將兩個配置組合在一起:
package soundsystem;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(CDPlayerConfig.class, CDConfig.class)
public class SoundSystemConfig{
}
說明:如果此時BlankDisc
配置在了XML
中(cd-config.xml
),如何讓spring
同時加載它和其他基于Java
的配置呢带兜?如下:
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig{
}
5.2 在XMl中配置引用JavaConfig
在XML
導(dǎo)入XML
配置如下:
<import resource="cd-config.xml">
在XML
中導(dǎo)入JavaConfig
配置如下:
<bean class="soundsystem.CDConfig">