1. 概述
本快速教程將討論 Spring 框架中引入的 @Primary 註解,該註解是在框架版本 3.0 中引入的。
簡單來説,我們使用 @Primary 來為同一類型的多個 Bean 提供更高的優先級。
讓我們更詳細地描述一下問題。
2. 為什麼需要@Primary?
在某些情況下,我們需要註冊相同類型的多個Bean。
在本例中,我們有JohnEmployee()和TonyEmployee()兩種類型的Employee Bean:
@Configuration
public class Config {
@Bean
public Employee JohnEmployee() {
return new Employee("John");
}
@Bean
public Employee TonyEmployee() {
return new Employee("Tony");
}
}Spring 在嘗試運行應用程序時會拋出NoUniqueBeanDefinitionException。
為了訪問同類型的 Bean,我們通常使用@Qualifier(“beanName”) 註解。
我們將它應用於注入點,與@Autowired 結合使用。 在我們的情況下,我們選擇在配置階段的 Bean,因此@Qualifier 無法在這裏應用。 您可以通過點擊鏈接瞭解更多關於@Qualifier 註解的信息。
為了解決此問題,Spring 提供@Primary 註解。
3. 使用 與
下面是配置類:
@Configuration
public class Config {
@Bean
public Employee JohnEmployee() {
return new Employee("John");
}
@Bean
@Primary
public Employee TonyEmployee() {
return new Employee("Tony");
}
}我們標記了TonyEmployee() 豆,並使用@Primary註解。 Spring 會優先注入TonyEmployee() 豆,而不是JohnEmployee() 豆。
現在,讓我們啓動應用上下文並從其中獲取Employee 豆。
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(Config.class);
Employee employee = context.getBean(Employee.class);
System.out.println(employee);運行應用程序後:
Employee{name='Tony'}我們可以看出,TonyEmployee() 實例在自動裝配時表現出偏好。
4. 使用 @Primary 與 @Component
我們可以直接在 Bean 上使用 @Primary。 讓我們來看一個示例:
public interface Manager {
String getManagerName();
}我們有一個 Manager 接口以及兩個子類 Bean,DepartmentManager:
@Component
public class DepartmentManager implements Manager {
@Override
public String getManagerName() {
return "Department manager";
}
}以及 總經理 Bean:
@Component
@Primary
public class GeneralManager implements Manager {
@Override
public String getManagerName() {
return "General manager";
}
}他們都覆蓋了 getManagerName() 方法,該方法位於 Manager 接口中。請注意,我們用 GeneralManager Bean 標記了 @Primary 註解。
這次,@Primary 註解僅在啓用組件掃描時才有意義:
@Configuration
@ComponentScan(basePackages="org.baeldung.primary")
public class Config {
}讓我們創建一個服務,用於在查找合適的 Bean 時使用依賴注入:
@Service
public class ManagerService {
@Autowired
private Manager manager;
public Manager getManager() {
return manager;
}
}這裏,豆子DepartmentManager和GeneralManager都符合自動裝配的條件。
由於我們標記了GeneralManager豆子為@Primary,因此它將被用於依賴注入:
ManagerService service = context.getBean(ManagerService.class);
Manager manager = service.getManager();
System.out.println(manager.getManagerName());輸出結果是“總經理”。
5. 關於 Bean 名稱的澄清
此外,在使用 @Primary 註解時,務必確保每個 Bean 名稱在上下文中是唯一的。 Spring 默認使用方法名作為 Bean 名稱。 雖然 @Primary 指示在存在歧義的情況下,應注入哪個 Bean,但它不允許同一配置類中存在同名的多個 Bean。
為了避免此類衝突,我們需要確保配置類中的每個方法都有一個唯一的名稱。 此外,我們還可以通過在 @Bean 註解中使用 name 屬性,顯式地指定名稱:
@Bean(name = "mercedesCar")
public Car car1() {
return new Car("Mercedes");
}
@Bean(name = "bmwCar")
@Primary
public Car car2() {
return new Car("BMW");
}在這種情況下,Bean 的名稱分別為 mercedesCar 和 bmwCar,以確保唯一性並避免衝突。
6. 結論
在本文中,我們學習了 Spring 的 @Primary 註解。通過代碼示例,我們展示了 @Primary 的必要性以及使用場景。