Spring中@Bean註解和@Configuration、@Component註解組合使用的差異

一、@Bean的“full”模式和“lite”模式     

        在一般常見情況下,@Bean註解在@Configuration類中聲明,稱之爲“full”模式;當@Bean註解和@Component註解組合使用時,稱之爲“lite”模式。這兩種組合使用的情況,初次看文檔時沒看明白,多看了幾次又跑了測試代碼,才大致理解了區別。 

二、兩種模式的差異

       如果只是把@Bean註解用在方法上,並且各個@Bean註解的方法之間沒有調用,上述兩種模式達到的效果基本相同,都可以把@Bean註解方法返回的對象作爲bean註冊到容器中。如果各個@Bean註解的方法之間有相互調用,那麼兩種模式就會有很大的區別-與full模式下的@Configuration不同,lite模式下 @Bean方法互相調用無法聲明Bean之間的依賴關係。這點在@Bean註解源碼註釋上也有說明。

1、“full”模式下@Bean方法互相調用


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;


@Configuration
public class Config {

    @Bean()
    public C c(){
        return new C();
    }

    @Bean
    public B b(){
        return new B(c());
    }

}

類B:

public class B {

    public C c;

    public B(C a){this.c=a;}

    public void shutdown(){
        System.out.println("b close...");
    }
}

類C:

public class C {

    private String name;

    @PostConstruct
    public void init(){
        this.name = "I am a bean";
    }

    @Override
    public String toString(){
        return "c say:" + name;
    }
}

       如上述所示代碼,我們有兩個類B和C,在@Configuration類中,使用兩個@Bean方法分別定義bean b 和bean c,但是需要注意的是new B(c()) 所在的@Bean方法調用了另一個@Bean方法。

測試主程序類:


import com.dxc.opentalk.springtest.config.B;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;


@EnableAspectJAutoProxy
@ComponentScan("com.dxc.opentalk.springtest")
public class BootStrap {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(BootStrap.class);
        B b = (B)applicationContext.getBean("b");
        System.out.println(b.c);
        System.out.println(applicationContext.getBean("c"));
        System.out.println(applicationContext.getBean("c").equals(b.c));
    }
}

程序輸出結果:

c say:I am a bean
c say:I am a bean
true

可以看出bean c 被注入到了bean b 中,bean b中的成員 c 確實是Spring容器中的bean。(其實,debug程序跟蹤Spring中的單例池更清晰,這裏採用輸出結果說明)

2、“lite”模式下@Bean方法互相調用

還是上面的代碼,我們只把Config類上的註解更換成@Component,其餘代碼保持不變:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;


@Component
public class Config {

    @Bean()
    public C c(){
        return new C();
    }

    @Bean
    public B b(){
        return new B(c());
    }

}

再看一下測試主程序類輸出結果:

c say:null
c say:I am a bean
false

可以看到bean b中的c只是一個普通的c對象,並不是Spring容器中的bean(b中的c沒有執行bean的初始化回調方法也和單例池中的c bean不相等)。所以這種模式下,@Bean方法互相調用不能完成bean之間相互依賴關係的注入。

三、總結

       綜上所述,在使用@Bean註解時需要注意兩種模式的區別,一般情況下@Bean都是和@Configuration註解搭配使用的。但是@Configuration註解的類會被Spring使用CGLIB子類化,所以@Configuration註解的類不能用 final 修飾,而@Component註解沒有此限制。如果使用@Bean註解和@Component註解組合需要完成bean之間相互依賴注入的話,官方推薦採用構造方法或者方法級別的依賴注入,如下:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Component
public final class Config {

    @Bean()
    public C c(){
        return new C();
    }

    @Bean
    public B b(C c){//構造方法注入
        return new B(c);
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章