1. 程式人生 > >Spring. 自動化裝配bean

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. 處理自動裝配的歧義性