天天看點

spring--裝配bean

衆所周知,spring最基本的功能就是用來管理bean,spring為我們提供了三種裝配bean的方式:
     1. XML顯式配置
     2. Java顯式配置
     3. 元件掃描和自動裝配

    一般來說,我們肯定是希望讓我們顯式配置的越少越好,是以,我們一般都會以元件掃描和自動裝配為主,Java顯式配置和XML顯式配置為輔,至于到底使用哪一種,還是看項目的需要和你個人的喜好.
           

1. 元件掃描

1.1 例子

spring通過元件掃描和自動裝配來幫助我們完成一些複雜的依賴關系,下面我們用一個CD播放器的例子來說明spring的元件掃描.
    首先,我們定義個CD播放機的接口:
           
public interface CompactDisc {
    void play();
}
           
然後,定義一個類去實作這個接口,這裡也沒有什麼特别的地方,主要就是我們通過@Component去修飾了該類,這個注解的作用就是告知spring,我們要将這個類作為一個bean,需要spring在容器中建立這個bean:
           
@Component
public class CompactDiscImpl implements CompactDisc {
    public void play() {
        System.out.println("play");
    }
}
           
不過,到目前為止,我們還沒有開啟spring的元件掃描功能,是以我們要啟用spring的元件掃描功能,有兩種方式可以開啟,一種是通過Java配置,一種是通過XML配置:
    Java配置,可以看出來@ComponentScan就是配置自動掃描的注解,該注解會預設掃描該配置類包和子包下的所有類,如果需要指定包去掃描,需配置:
           
@Configuration
@ComponentScan
public class SpringConfig {
}
           
XML配置中,隻需要加一句話即可,<context:component-scan base-package="指定包">.

    之後,我們就可以測試該例子,建立一個JUnit單元測試,@RunWith(SpringJUnit4ClassRunner.class)的作用就是測試開始時自動建立Spring上下文,@ContextConfiguration(classes = SpringConfig.class)的作用就是告訴Spring從SpringConfig類中加載配置資訊:
           
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class Test {

    @Autowired
    private CompactDisc cd;

    @org.junit.Test
    public void test() {
        Assert.assertNotNull(cd);
    }
}
           
運作一下,應該會發現測試通過,因為cd通過Spring的@Autowired自動注入進去了,是以不為null.
           

1.2 細節配置

Spring應用上下文為每一個bean都會有一個給定的id,在上面的例子中,雖然我們沒有明确的給CompactDiscImpl設定id,但是Spring會預設給其設定一個id,就是compactDiscImpl,就是将首字母小寫.
    如果你想為這個bean設定不同的id,可以這樣做:@Component("myId"),也就是利用@Component注解來為bean自定義id.

    上例中,還有一個注解@ComponentScan,該注解是開啟自動掃描注解,會預設掃描所修飾類所在包和其子包,當然,我們也可以指定具體的包進行掃描,可以這樣制定:@ComponentScan("myPackage").
    當然,我們可以更為清晰的指定@ComponentScan(basePackageClasses = "myPackage"),如果你想指定多個包呢,那也是可以的,如@ComponentScan(basePackageClasses = {"myPackage1","myPackage2"}).
    有些時候,利用字元串指定所要掃描的包是不安全的,比如你重構項目時,包可能會發生改變,是以我們也可以通過指定類或接口去掃描該類或接口所在的包,例如:@ComponentScan(basePackageClasses = {"MyClass1.class","MyClass2.class"}),通常我們會建立一個空接口放在該包下,然後去用這種注解方式去引用,防止在重構過程中,删除了使用的業務相關類.
           

2. 自動裝配

簡單的說,自動裝配就是Spring将類的依賴關系自動組合在一起,比如A類依賴B類,如果使用自動裝配,Spring就會将B自動注入到A中,而不用我們在A類中執行個體化B類.

    Spring中一般是使用@Autowired注解來實作自動裝配,@Autowired注解可以用在類的任何方法上,不管是構造器,還是set方法,還是其他方法,Spring都會去滿足方法參數上所申明的依賴.
    假如有且隻有一個bean比對依賴的需求的話,那麼這個bean将會被裝配進來.
    如果沒有比對的bean,那麼在應用上下文建立的時候,Spring會抛出一個異常,為了避免異常的出現,你可以将@Autowired的required屬性設定為false:@Autowired(required=false),但是這意味着如果沒有比對到合适的bean,該bean将會為null,調用時應該加以判斷,否則會出現空指針異常.
    如果比對到多個合适的bean的話,Spring也會抛出異常,表明沒有明确指定選擇哪個bean進行裝配.
           

3. 通過Java代碼裝配bean

從理論上來說,我們應該都采用元件掃描和自動裝配的方式來進行自動化配置,但難免有一些特殊情況,比如我們要使用第三方包的元件裝配到我們的應用中,但這種情況下,我們無法再它的類上添加@Component或者是@Autowired,是以就不能使用自動裝配,而要采用顯式的配置.顯式配置有兩種方式,一種是通過Java代碼,一種是通過XML配置檔案,這裡我們首先介紹使用Java代碼來裝配bean.

    也許你還記得,在前面的例子中,有一個SpringConfig,這個類使用了@Configuration注解,這個注解就是申明該類是一個配置類,該類包含了Spring應用上下文如何建立bean.
    例如,我們這裡有A類和B類,A類依賴于B類,我們在配置類中就可以這樣寫:

    @Bean注解會告訴Spring,這個方法将會傳回一個對象,這個對象要注冊成為Spring應用上下文中的bean.
    當然,前面我們也提到過,每個bean都是有一個id的,下面這個例子中,B的bean的id就是getB,也就是方法名,如果你要自己自定義的話,可以使用@Bean(name = "myName"),并且因為是使用Java代碼來進行配置,我們可以在return new B();之前做非常多的事情,比如B有很有子類,我們可以随機構造一個B的子類執行個體傳回.
    因為A依賴于B,是以我們希望将B注入到A中,我們可以使用return new A(getB()),這樣使用時,其實Spring并不會去調用getB()方法,因為被@Bean修飾的方法的調用都會被阻斷,當我們使用getB()方法時,其實是Spring阻斷了該方法的調用,并且傳回了Spring所建立的bean.  
    當然,注入方法一的使用十分局限,比如我兩個Bean并沒有寫在一個配置類中,或者B是自動掃面的,或者B是XML所配置的,是以,我們還是應該使用注入方法二,這意味着隻要B這個bean存在于Spring應用上下文中,不管是怎麼建立出來的,我都可以将其注入到A中.
           
@Configuration
public class JavaConfig{
    @Bean
    public B getB(){
        return new B();

    // 注入方法一
    @Bean
    public A getA(){
        return new A(getB());
    }

    //注入方法二
    @Bean
    public A getA(B b){
        return new A(b);
    }
}
           

4. 通過XML裝配bean

XML配置在三種配置上來說,是屬于比較繁瑣的,而且基本上XML配置能實作的,Java配置都可以實作,Java配置可以實作的,XML缺實作不了(就比如說之前例子中,可以随機裝配B類子類的bean).
    但是由于目前,還是有非常多的項目是使用XML配置來裝配bean的,是以此處也會介紹一些XML配置的基本用法.


    要在XML配置檔案中申明一個bean,我們可以使用<bean>,并使用class辨別類的位置,使用id來申明該bean的id,Spring預設會調用該類的預設構造器來建立該bean:
           
當我們要使用XML進行bean注入時,可以這樣做,我們依然使用A類依賴于B類的例子:
    <constructor-arg>是使用構造器注入,當Spring執行個體化A類時,<constructor-arg>會告知Spring,A類需要一個id為b的bean傳入到其構造器中.(如果構造器需要多個參數,就寫多個<constructor-arg ref="xxx"/>即可)
    有時候我們可能需要傳入一些字面量注入到類中,比如字元串,這時我們可以使用<constructor-arg value="string"/>,ref換成了value.
           
<bean id="b" class="b class position"/>

<bean id="a" class="a class position">
    <constructor-arg ref="b"/>
</bean>
           
除了使用構造器來注入bean,我們也可以使用set方法來注入bean,property中的name就是屬性的名稱,ref就是該屬性引用的bean的id:
           
<bean id="b" class="b class position"/>

<bean id="a" class="a class position">
    <property name="b" ref="b" />
</bean>
           

5. 導入和混合配置

在實際的開發中,我們可能會遇到一些特殊的情況:

    1.我們使用Java配置的時候,如果配置資訊過多,我們要分到好幾個配置類中進行配置,我們可以使用@Import(xxx.class)将配置類導入到其他配置類中.這裡假設我們有A和B兩個配置類.
           
@Configuration
public class A{
}
           
@Configuration
@Import(A.class)
public class B{
}
           
或者我們使用第三個空的配置類C來導入A和B兩個類.

    2.我們要在Java配置中引用XML配置,我們可以使用注解@ImportResource:
           
@Configuration
@Import(A.class)
@ImportResource("classpath:spring.xml")
public class B{
}
           
3.在XML配置中引用XML配置:
           
4.在XML配置中引用Java配置,可以直接将Java配置類定義為一個<bean>


    到這,spring的裝配bean就介紹完了.