Spring. 自動化裝配bean
Spring從兩個角度來實現自動化裝配:
- 元件掃描( component scanning ):Spring會自動發現應用上下文中所建立的 bean。
- 自動裝配( autowiring ):Spring自動滿足 bean 之間的依賴
1、建立可被發現的bean
建立一個介面CompactDisc:
package soundsystem;
public interface CompactDisc{
void play();
}
建立帶有@Component註解的CompactDisc介面的一個實現類,這個類會被Spring掃描到並自動建立bean
package soundsystem; import ... @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); } }
使用@ComponentScan註解啟動元件掃描,@ComponentScan 預設會掃描與配置類相同的包以及其下所有的子包,這樣,被@Component註解修飾的類就會被發現
package soundsystem;
import ...
@Configuration
@ComponentScan
public class CDPlayerConfig{
}
也可以使用XML配置的方式來啟動元件掃描
<?xml version="1.0" encoding="UTF-8"?> <beans ... ... > <context:component-scan base-package="soundsystem"/> </beans>
2.為元件掃描的 bean 命名
若直接使用 @Component 註解來宣告一個 bean,bean 的名字預設為類名首字母小寫。
例如,如下 SgtPeppers 類的預設 bean 名稱為 sgtPeppers
@Component()
public class SgtPeppers implements CompactDisc{
...
}
可以在 @Component 註解中說明此 bean 的名稱:
@Component("lonelyHeartClub") public class SgtPeppers implements CompactDisc{ ... }
此外,也可以使用另外一種為 bean 命名的方式:
package soundsystem;
import javax.inject.Named;
@Named("lonelyHeartClub")
public class SgtPeppers implements CompactDisc{
...
}
Spring 支援將 @Named 作為 @Component 註解的替代方案,兩者之間有一些細微的差異,但在大多數場景下,它們是可以相互替換的。
推薦使用 @Component 註解,因為 @Named 註解並不能清楚的表明它是做什麼的
3.設定元件掃描的基礎包
在之前的案列中,我們沒有為 @ComponentScan 註解設定任何屬性,這意味著,按照預設規則,它會以配置類所在包作為基礎包( base package )來掃描元件.
有一個原因會促使我們要明確的設定基礎包,那就是我們想要將配置類放在單獨的包中,使其與其他的應用程式碼區分開來。
為了指定不同的包,只需要在 @ComponentScan 的 value 屬性中指明包的名稱:
@Configuration
@ComponentScan("soundsystem")
public class CDPlayerConfig{
}
或者,如果你想更加明確的表名你所設定的是基礎包,那麼你可以通過 basePackages 屬性進行配置:
@Configuration
@ComponentScan(basePackages="soundsystem")
public class CDPlayerConfig{
}
如果你想掃描多個包,只需要將 basePackages 屬性的值設定為要掃描包的一個數組即可:
@Configuration
@ComponentScan(basePackages={"soundsystem","video"})
public class CDPlayerConfig{
}
注意到,basePackages 屬性的值是一個 String 型別的陣列,這樣配置沒有問題,但卻是不安全的,如果要重構程式碼的話,這些包的名字可能會被修改,從而導致包掃描出現錯誤,除了將包設定為簡單的 String 型別之外,@ComponentScan 還提供了另一種方法,那就是將其指定為要掃描包中所含的類或介面。
@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig{
}
你可以在包中設定一個專門用來進行包掃描的空標記介面,這樣,可以避免對任何實際應用的程式碼進行重構後,包掃描出現錯誤。
4.通過為 bean 添加註解實現自動裝配
@Component
public class CDPlayer implements MediaPlayer{
private CompactDisc cd;
@Autowired
public CDPlayer(CompacrDisc cd){
this.cd = cd;
}
public void play(){
cd.play();
}
}
以上示例的構造器上添加了 @Autowired 註解,這表明當 Spring 建立 CDPlayer bean 的時候,會通過這個構造器進行例項化並傳入一個 CompactDisc 型別的 bean。
@Autowired 註解不僅能夠用在構造器上,還可以用在屬性設定的 Setter 方法上。
實際上,Setter 方法並沒有什麼特殊之處,@Autowired 註解可以用在類的任何方法上:如下,@Autowired 註解完全能夠發揮作用
@Autowired
public void insertDisc(CompactDisc cd){
this.cd = cd;
}
不管是構造器、Setter 方法還是其他方法,Spring 都會嘗試滿足方法引數上所宣告的依賴,假如有且僅有一個 bean 依賴需求的話,那麼這個 bean 就會被裝填進來。
如果沒有匹配的 bean,那麼在應用上下文建立的時候,Spring 會丟擲一個異常。為了避免異常的出現,你可以將 @Autowired 的 required 屬性設定為 false:
@Autowired(required=false)
public CDPlayer(CompactDisc cd){
this.cd = cd;
}
如果有多個 bean 都能滿足依賴關係的話,Spring 也將會丟擲一個異常,表明沒有明確指定要選擇那個 bean 進行裝配。
處理自動裝配的歧義性問題,請參閱我的另一篇文章:Spring. 處理自動裝配的歧義性