參考
1. Bean(不僅僅指@Bean)是什么?
bean在spring中可以理解為一個(gè)對(duì)象。理解這個(gè)對(duì)象需要換一種角度蚌堵,即可將spring看做一門編程語(yǔ)言,@Bean是spring語(yǔ)言聲明對(duì)象的標(biāo)識(shí)促脉。
spring啟動(dòng)過(guò)程中會(huì)自動(dòng)掃描注解辰斋,當(dāng)遇到能產(chǎn)生Bean的注解(見下方列表)后策州,會(huì)將注解的類自動(dòng)實(shí)例化(自動(dòng)掃描及實(shí)例化只進(jìn)行一次),之后將這個(gè)類的實(shí)例放到spring框架的容器中宫仗,當(dāng)需要使用時(shí)(自動(dòng)裝配)會(huì)從容器中調(diào)用這個(gè)實(shí)例够挂。
調(diào)用加了注解的類A且該類A中也有自動(dòng)裝配的bean時(shí),不能使用new A()
的方式藕夫,否則A中自動(dòng)裝配的bean都會(huì)失效孽糖,需要使用@Autowired A a;
才行。
2. 什么注解能產(chǎn)生Bean?
一般為需要的類都添加注解毅贮。
能產(chǎn)生Bean的注解有:
(這些注解產(chǎn)生的Bean各有不同办悟,可參考文末資料。)
- @Component
- @Repository
- @Controller
- @Service
- @Configration
前五種只能對(duì)類使用滩褥。 - @Bean (特殊病蛉,可對(duì)方法注解。@Bean注解需在上方五個(gè)注解的類中才生效瑰煎,例如下:
3. Bean的用法舉例
// Class B
// 此處用一個(gè)任意Bean類(上文的第1個(gè)到第5個(gè))注解
@Component
public class B {
int testValue;
}
// Class A
// 此處用一個(gè)任意Bean類(上文的第1個(gè)到第5個(gè))注解
@Component
public class A {
@Bean
public B b(){
B b = new B();
// b的自定義處理代碼
b.testValue = 123;
return b;
}
}
// Class C
// 此處用一個(gè)任意Bean類注解
@Service
public class C {
public static B b; // 聲明為static
@Autowired // 自動(dòng)裝配方式1
public void setB(B b) {
System.out.println("B自動(dòng)裝配");
TestController.b = b;
System.out.println("b.testValue" + b.testValue); // 此處打印結(jié)果為 123
TestController.b.testValue = 456;
System.out.println("b.testValue" + b.testValue); // 此處打印結(jié)果為 456
// 此處铺然,b為經(jīng)過(guò)在A類中@Bean注解的方法b()中初始化處理過(guò)的實(shí)例
}
}
4. 自動(dòng)裝配是什么?
創(chuàng)建應(yīng)用對(duì)象之間協(xié)作關(guān)系的行為稱為裝配酒甸。也就是說(shuō)當(dāng)一個(gè)類A的屬性中聲明了另一個(gè)類B的對(duì)象魄健,A實(shí)例化時(shí),需要為A的屬性B進(jìn)行實(shí)例化插勤。這就是裝配沽瘦。
自動(dòng)轉(zhuǎn)配會(huì)自動(dòng)將對(duì)象屬性實(shí)例化。
在A類中聲明屬性B時(shí)加上注解@Autowired农尖,A實(shí)例化時(shí)spring會(huì)自動(dòng)從容器中調(diào)動(dòng)B的實(shí)例析恋。為了讓spring能從容器中調(diào)用B的實(shí)例,需在B的類聲明上有能產(chǎn)生Bean的注解卤橄。
5. 兩種自動(dòng)裝配的注解
自動(dòng)裝配的注解有:
(這些注解裝配功能各有不同绿满,可參考文末資料。)
@Autowired (通過(guò)類裝配窟扑,一般用這個(gè))
@Resource (通過(guò)自命名裝配)
(兩者區(qū)別見:Spring注解Resource和Autowired區(qū)別對(duì)比)
6. @Autowired的兩種裝配方式
@Autowired自動(dòng)裝配有兩種方式:
一種是重寫set方法喇颁,可對(duì)對(duì)象自定義操作,B中屬性testValue可自行初始化嚎货,見上方類C中代碼橘霎。
另一種直接使用@Autowired注解聲明,不能對(duì)聲明對(duì)象自定義操作殖属,即B中屬性testValue未初始化姐叁,方式如下:
// Class A
public class A {
@Autowired
private B b; // 不能聲明為static
}
// Class B
// 此處用一個(gè)任意Bean類注解
@Service
public class B {
int testValue;
}
這樣,當(dāng)在某處實(shí)例化A時(shí),spring會(huì)自動(dòng)從容器中為A裝配對(duì)象b外潜,但是b.testValue未初始化原环。
7. Bean的初始化
- 總結(jié)自 @Autowired的使用:推薦對(duì)構(gòu)造函數(shù)進(jìn)行注釋,寫的不錯(cuò)处窥。
- java spring使用@Autowired與構(gòu)造器進(jìn)行變量初始化總結(jié)了三種初始化方法嘱吗,但第一種(在成員變量上注釋@Autowired)其實(shí)是不被推薦的,理由見后文滔驾。
Java變量的初始化順序?yàn)椋红o態(tài)變量或靜態(tài)語(yǔ)句塊–>實(shí)例變量或初始化語(yǔ)句塊–>構(gòu)造方法–>@Autowired
因此建議
private User user;
private String school;
@Autowired
public UserAccountServiceImpl(User user){
this.user = user;
this.school = user.getSchool();
}
而不是
@Autowired
private EnterpriseDbService service;
因?yàn)橐韵虑闆r會(huì)報(bào)錯(cuò):
@Autowired
private User user;
private String school;
public UserAccountServiceImpl(){
this.school = user.getSchool();
}
報(bào)錯(cuò)信息可能會(huì)像下面:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name '...' defined in file [....class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Constructor threw exception; nested exception is java.lang.NullPointerException
因?yàn)镴ava類會(huì)先執(zhí)行構(gòu)造方法谒麦,然后再給注解了@Autowired 的user注入值,所以在執(zhí)行構(gòu)造方法的時(shí)候哆致,就會(huì)報(bào)錯(cuò)绕德。
@Configuration和@TestConfiguration
@Bean是一個(gè)方法級(jí)別上的注解,主要用在@Configuration注解的類里摊阀,也可以用在@Component注解的類里耻蛇。添加的bean的id為方法名
下面是@Configuration里的一個(gè)例子
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
這就相當(dāng)于xml文件里面的配置
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
@TestConfiguration作用類似@Configuration,但只是應(yīng)用于單元測(cè)試驹溃,在正式部署時(shí)城丧,該注解所標(biāo)注的類會(huì)被忽略延曙。