@Autowired自動裝配,@Bean註入@Primary,@Qualifier優先級講解
Autowired自動裝配
spring利用依賴註入(DI),完成對IOC容器中的各個組件的依賴關系賦值
對同一個Dao類,既有 @Bean 註解聲明,又有Autowired 自動裝配,分析一下幾種情況:
第一種情況
1、如果Dao類中聲明瞭@Repository,且@ComponentScan 添加瞭dao掃描,則默認會創建一個testDao在IOC容器中。
2、如果在config中指定瞭Bean註解,此時:
a、如果Bean註解的方法名也是testDao ,則會覆蓋默認對象
1.新建maven工程
<groupId>com.MySpring</groupId> <artifactId>demo</artifactId>
新建TestController.java TestService.java TestDao; 分別建在指定的包內
pom.xml 文件如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.MySpring</groupId> <artifactId>demo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.6.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> </project>
1.TestController 類:
package com.MySpring.demo.controller; import com.MySpring.demo.service.TestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class TestController { @Autowired private TestService testService; }
2.TestDao 類:
package com.MySpring.demo.dao; import org.springframework.stereotype.Repository; @Repository public class TestDao { public String getMessage( ) { return message; } public void setMessage( String message ) { this.message = message; } private String message ="1"; @Override public String toString( ) { return "TestDao{" + "message='" + message + '\'' + '}'; } }
3.TestService
package com.MySpring.demo.service; import com.MySpring.demo.dao.TestDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class TestService { @Autowired private TestDao testDao; public void print(){ System.out.printf( "[%s] from testService.\n" ,testDao); } }
4.AppConfig
package com.MySpring.demo; import com.MySpring.demo.dao.TestDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan({"com.MySpring.demo.controller","com.MySpring.demo.service","com.MySpring.demo.dao",}) public class AppConfig { @Bean public TestDao testDao(){ TestDao dao = new TestDao(); dao.setMessage( "2" ); return dao; } }
5.新建測試類:
package com.MySpring.demo; import com.MySpring.demo.dao.TestDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan({"com.MySpring.demo.controller","com.MySpring.demo.service","com.MySpring.demo.dao",}) public class AppConfig { @Bean public TestDao testDao(){ TestDao dao = new TestDao(); dao.setMessage( "2" ); return dao; } }
輸出結果:
第二種情況
如果IOC容器中有兩個Dao實例,則@Autowired 自動裝配會根據變量名稱去IOC容器中尋找,對應的實例裝載,若找不到則報錯,默認是必須存在,Autowired 源碼如下:
當然,也可以設置required為false
public @interface Autowired { /** * Declares whether the annotated dependency is required. * <p>Defaults to {@code true}. */ boolean required() default true; }
1. 更改AppConfig類中 Bean註解的方法名為testDao2
2. 同時更改TestService中的 testDao變量名為 testDao2,其他保持不變:
執行測試用例AppTest,得到結果,我們可以發現IOC容器中,有兩個實例名稱,Service中Autowired 自動裝配的對象變成瞭2, 而通過getBean(“testDao”) 得到的結果是Dao原始的結果1:
第三種情況
在第二中情況下,如果在@Autowired 註解的變量中聲明@Qualifier(“testDao”),則會優先根據Qualifier指定的名稱去裝載Bean對象, 修改TestService代碼如下:
運行AppTest測試用例,結果如下:
第四種情況
@Qualifier與@Primary註解同時存在 在第三種情況基礎上,@Bean註解中追加 @Primary註解
更改 AppTest 中獲取 TestDao的方式為 app.getBean(TestDao.class) 如下:
運行AppTest 測試用例,結果如下:
證明:
@Qualifier是根據bean id指定獲取testDao, 不受@Primary影響. 但是在沒有Qualifier 的地方,通過@Primary標記的Bean優先被使用。
通過TestDao{message=‘2′} 可以證明,如果不標記Primary Bean,則此處會報錯,因為有兩個相同實例,Spring不知道該選擇哪個。
總結
1、如果Dao類中聲明瞭@Repository,且@ComponentScan 添加瞭dao掃描,則默認會創建一個testDao在IOC容器中。
2、如果在config中指定瞭Bean註解,此時:
a、如果Bean註解的方法名也是testDao ,則會覆蓋默認對象
b、如果Bean註解的方法名或者聲明的別名不是testDao ,則會新創建一個對象在IOC容器中。
@Autowired 取值
如果聲明的變量是testDao ,則默認會去IOC容器中獲取testDao對象。
如果聲明的變量是Bean註解指定的別名或者方法名,則會去容器中獲取Bean註解別名或者方法名。
如果聲明 的變量不是以上兩者,則會報錯
app.getBean()
如果在IOC容器中存在兩個TestDao實例,則隻能通過字符串名稱來獲取bean實例,如:app.getBean(“testDao”),否則無法獲取到對象, 有指定@Primary聲明除外。
如果在IOC容器中隻存在一個TestDao實例,則可以通過app.getBean(TestDao.class)獲取bean對象。
@Primary 優先
隻要Primary出現,不管是Autowired 還是getBean取值,都會優先取到 @Primary 聲明的那個容器對象實例,但是,
如果在Autowired中聲明瞭@Qualifier(“testDao”),則在Autowired 上的取值會優先Qualifiler對象,其他地方還是會去取Primary對象
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- 關於@Configuration的作用說明
- Spring框架學習之Spring @Autowired實現自動裝配的代碼
- Spring使用IOC與DI實現完全註解開發
- Spring純註解開發模式讓開發簡化更簡化
- spring IOC控制反轉原理詳解