关于Qualifier你要知道的二三事
当使用Spring框架进行Java应用程序开发时,可能会遇到ConflictingBeanDefinitionException异常。如:这个异常通常发生在以下情况下:在应用程序上下文中存在多个相同名称的Bean定义,导致Spring无法确定应该使用哪一个Bean。这个问题可能出现在团队协作开发中,特别是当不同的开发者在不同的模块中定义了相同名称的Bean时。在本文中,我们将探讨这个异常出现的原因
theme: cyanosis

🥯 @Qualifier注解的常用写法
- 指定Bean名称
@Bean(value = "b1") B b1() - 不指定Bean名称
@Bean("b2") B b2()
使用
@Bean注解来标记一个方法,该方法的返回值将被注册为一个 Bean。
value = "b1"指定了 Bean 的名称为 “b1”。这个名称将用作容器中的 Bean 名称。如果不指定
value,则默认使用方法名作为 Bean 的名称。
🍞 @Qualifier注解的作用-定义Bean-指定Bean的名称
@Qualifier注解可以区分具有相同类型的多个Bean,用于明确指定要注入的Bean的名称或限定符。通过为要注入的Bean添加 @Qualifier注解,你可以告诉Spring应该使用哪个Bean,以解决Spring框架中依赖注入时的歧义性问题。
假设我们有一个名为"UserService"的服务接口,有多个实现类可以用于不同的业务逻辑。现在,我们需要在另一个类中注入"UserService",但由于有多个实现类可用,我们需要明确指定要注入的具体实现类。
示例代码如下:
public interface UserService {
void performAction();
}
@Component
@Qualifier("implementationA")
public class UserServiceImplA implements UserService {
public void performAction() {
System.out.println("Performing action with UserServiceImplA");
}
}
@Component
@Qualifier("implementationB")
public class UserServiceImplB implements UserService {
public void performAction() {
System.out.println("Performing action with UserServiceImplB");
}
}
@Component
public class UserClient {
@Autowired
@Qualifier("implementationA")
private UserService userService;
public void doSomething() {
userService.performAction();
}
}
错误的案例:
@Component
public class UserClient {
@Autowired
private UserService userService;
public void doSomething() {
userService.performAction();
}
}
明确:@Autowired注解是按照类型注入Bean。由于UserServiceImplA、UserServiceImplB都是实现自接口UserService,因此在Spring注入过程中,会导致Spring不知道该注入哪个Bean的实现。 注入时,如果不使用该注解,会报以下的异常:

NoUniqueBeanDefinitionException异常是Spring框架中的一个异常,它表示在自动装配(Autowired)过程中无法确定要注入的bean,因为有多个符合条件的bean可用。
在上面的示例中,我们定义了一个"UserService"接口,并有两个实现类"UserServiceImplA"和"UserServiceImplB"。在"UserClient"类中,我们使用@Autowired注解注入"UserService"接口,并使用@Qualifier注解标记要注入的具体实现类。
通过@Qualifier(“implementationA”),我们明确指定了要注入的是"UserServiceImplA"实现类。这样,当"UserClient"类中使用"userService"时,将调用"UserServiceImplA"的实现方法。
通过@Qualifier注解,我们能够精确选择要注入的特定Bean,解决了存在多个相同类型的Bean时的歧义问题。
🥐 同类型的Bean注入知识拓展
如果存在多个Bean,它们具有相同的类型但不同的名称,那么在注入这些Bean时不会抛出NoUniqueBeanDefinitionException异常。相反,Spring会根据类型进行自动装配,选择与目标类型匹配的Bean进行注入。
以下是一个示例,演示具有相同类型但不同名称的多个Bean的自动装配:
@Component("userService")
public class UserService {
public void performAction() {
System.out.println("Performing action in UserService");
}
}
@Component("adminService")
public class AdminService {
public void performAction() {
System.out.println("Performing action in AdminService");
}
}
现在,我们可以在另一个类中使用@Autowired注解来注入这两个Bean:
@Component
public class ServiceClient {
@Autowired
private UserService userService;
@Autowired
private AdminService adminService;
public void performServiceAction() {
userService.performAction();
adminService.performAction();
}
}
在上述示例中,ServiceClient类成功地注入了UserService和AdminService两个不同名称但相同类型的Bean,因为它们都是被@Component注解标记的Spring管理的Bean。
总结起来,当存在具有相同类型但不同名称的多个Bean时,Spring可以根据类型进行自动装配,选择合适的Bean进行注入,而不会抛出异常。只有当存在多个相同类型的Bean且没有提供明确的标识符时,才会抛出NoUniqueBeanDefinitionException异常。
🥖 @Qualifier注解的作用-注入Bean-配合@Configuration使用
当在一个业务Bean中存在多个同类型的Bean时,可以使用@Qualifier注解来指定要注入的具体Bean。
@Configuration
public class AppConfig {
@Bean
@Qualifier("database")
public DataSource dataSource() {
// 返回一个数据库数据源的实现
return new DatabaseDataSource();
}
@Bean
@Qualifier("file")
public DataSource fileDataSource() {
// 返回一个文件数据源的实现
return new FileDataSource();
}
@Bean
public DataProcessor dataProcessor(@Qualifier("database") DataSource dataSource) {
// 通过@Qualifier注解指定要注入的数据源为"database"
return new DataProcessor(dataSource);
}
}
public interface DataSource {
void connect();
void disconnect();
}
public class DatabaseDataSource implements DataSource {
@Override
public void connect() {
System.out.println("Connecting to the database...");
// 连接数据库的具体实现逻辑
}
@Override
public void disconnect() {
System.out.println("Disconnecting from the database...");
// 断开数据库连接的具体实现逻辑
}
}
public class FileDataSource implements DataSource {
@Override
public void connect() {
System.out.println("Connecting to the file system...");
// 连接文件系统的具体实现逻辑
}
@Override
public void disconnect() {
System.out.println("Disconnecting from the file system...");
// 断开文件系统连接的具体实现逻辑
}
}
在上面的例子中,通过@Configuration注解标记了一个配置类AppConfig。在该配置类中定义了两个名为dataSource和fileDataSource的Bean方法,并分别使用@Qualifier注解指定了它们的限定符为"database"和"file"。
接着,在dataProcessor方法上,使用@Qualifier(“database”)注解来指定要注入的数据源为名为"database"的Bean。这样,在其他地方使用@Autowired或@Inject注解注入DataProcessor时,就会根据@Qualifier注解指定的限定符来选择正确的Bean进行注入。
错误示例
@Data
public class B {
private String name;
}
@Configuration
@ComponentScan
public class JavaConfig {
@Bean(value = "b1")
@Qualifier
B b1() {
return new B();
}
@Bean(value = "b2")
@Qualifier
B b2() {
return new B();
}
}
@Component
public class A {
@Autowired
@Qualifier
B b;
}
此时,虽然IDEA中不会明确告警,在编译期也不会出现问题,但是在启动Spring项目时会报错:

原因如下:
相同类型的B Bean在JavaConfig中配置,在A类中进行注入,A类同时也是用了@Qualifier限定符,但是在限定符中未明确使用哪个B类型哪个名称的Bean,@Autowired注入时,依然是根据类型去匹配,找到两个同类型的Bean,但是产生歧义,因此报错。
🥓 总结
由此可见,关于 @Qualifier注解,在Spring中的主要作用有两个:
- 在定义Bean的地方,使用@Qualifier注解为Bean添加一个限定符,这个限定符可以是任何字符串,用于区分同一类型的多个Bean。
@Bean
@Qualifier("database")
public DataSource dataSource() {
// 返回一个数据库数据源的实现
return new DatabaseDataSource();
}
- 在需要进行注入Bean的地方,使用@Qualifier注解并指定相同的限定符值,具体来说,假设有两个名为"databaseDataSource"和"fileDataSource"的DataSource类型的Bean,并且它们都使用了@Qualifier注解并指定了不同的限定符值。
@Autowired
@Qualifier("database")
private DataSource dataSource;
🫓 从源码中看@Qualifier注解
内容待完善中…
写在最后
感谢您读到这里,如果觉得文章写的还不错或有任何疑问,可以添加我微信,我随时为您解答。

更多推荐


所有评论(0)