一、@Bean的“full”模式和“lite”模式
在一般常見情況下,@Bean注解在@Configuration類中聲明,稱之為“full”模式;當(dāng)@Bean注解和@Component注解組合使用時,稱之為“lite”模式。
這兩種組合使用的情況,初次看文檔時沒看明白,多看了幾次又跑了測試代碼,才大致理解了區(qū)別。
二、兩種模式的差異
如果只是把@Bean注解用在方法上,并且各個@Bean注解的方法之間沒有調(diào)用,上述兩種模式達(dá)到的效果基本相同,都可以把@Bean注解方法返回的對象作為bean注冊到容器中。
如果各個@Bean注解的方法之間有相互調(diào)用,那么兩種模式就會有很大的區(qū)別-與full模式下的@Configuration不同,lite模式下 @Bean方法互相調(diào)用無法聲明Bean之間的依賴關(guān)系。
這點在@Bean注解源碼注釋上也有說明。
1、“full”模式下@Bean方法互相調(diào)用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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:
1
2
3
4
5
6
7
|
public class B { public C c; public B(C a){ this .c=a;} public void shutdown(){ System.out.println( "b close..." ); } } |
類C:
1
2
3
4
5
6
7
8
9
10
11
12
|
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方法調(diào)用了另一個@Bean方法。
測試主程序類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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)); } } |
程序輸出結(jié)果:
c say:I am a bean
c say:I am a bean
true
可以看出bean c 被注入到了bean b 中,bean b中的成員 c 確實是Spring容器中的bean。(其實,debug程序跟蹤Spring中的單例池更清晰,這里采用輸出結(jié)果說明)
2、“lite”模式下@Bean方法互相調(diào)用
還是上面的代碼,我們只把Config類上的注解更換成@Component,其余代碼保持不變:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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()); } } |
再看一下測試主程序類輸出結(jié)果:
c say:null
c say:I am a bean
false
可以看到bean b中的c只是一個普通的c對象,并不是Spring容器中的bean(b中的c沒有執(zhí)行bean的初始化回調(diào)方法也和單例池中的c bean不相等)。所以這種模式下,@Bean方法互相調(diào)用不能完成bean之間相互依賴關(guān)系的注入。
三、總結(jié)
綜上所述,在使用@Bean注解時需要注意兩種模式的區(qū)別,一般情況下@Bean都是和@Configuration注解搭配使用的。
但是@Configuration注解的類會被Spring使用CGLIB子類化,所以@Configuration注解的類不能用 final 修飾,而@Component注解沒有此限制。
如果使用@Bean注解和@Component注解組合需要完成bean之間相互依賴注入的話,官方推薦采用構(gòu)造方法或者方法級別的依賴注入,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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){ //構(gòu)造方法注入 return new B(c); } } |
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/qq_22076345/article/details/104687011