@Primary

在spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下 @Primary  的作用就出来了。下面是个简单的使用例子。
 

public interface Singer {
    String sing(String lyrics);
}

有下面的两个实现类:

@Component // 加注解,让spring识别
public class MetalSinger implements Singer{

    @Override
    public String sing(String lyrics) {
        return "I am singing with DIO voice: "+lyrics;
    }
}

//注意,这里没有注解
public class OperaSinger implements Singer {
    @Override
    public String sing(String lyrics) {
        return "I am singing in Bocelli voice: "+lyrics;
    }
}

下面就是注入上面的接口实现类:

@Component
public class SingerService {
    private static final Logger logger = LoggerFactory.getLogger(SingerService.class);

    @Autowired
    private Singer singer;

    public String sing(){
        return singer.sing("song lyrics");
    }
}

结果是什么呢?
I am singing with DIO voice: song lyrics. 原因很简单,就是 OperaSinger 这个类上面根本没有加上注解@Copmonent 或者 @Service, 所以spring 注入的时候,只能找到 MetalSinger 这个实现类. 所以才有这个结果。

但是如果一旦 OperaSinger 这个类加上了@Copmonent 或者 @Service 注解,有趣的事情就会发生,你会发现一个错误的结果或异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [main.service.Singer] is defined: expected single matching bean but found 2: metalSinger,operaSinger

提示很明确了,spring 根据类型无法选择到底注入哪一个。这个时候@Primay 可以闪亮登场了。

@Primary
@Component
public class OperaSinger implements Singer{

    @Override
    public String sing(String lyrics) {
        return "I am singing in Bocelli voice: "+lyrics;
    }
}

如果代码改成这样,再次运行,结果如下:
"I am singing in Bocelli voice: song lyrics", 用@Primary 告诉spring 在犹豫的时候优先选择哪一个具体的实现。

### 关于 `@Primary` 注解在 Spring Boot 中与 RabbitMQ 集成时的用法 在 Spring Boot 应用程序中,当集成 RabbitMQ 并使用多个连接工厂或消息监听器容器时,可能会遇到需要指定默认 Bean 的情况。此时,`@Primary` 注解可以帮助解决这种歧义问题。 #### 什么是 `@Primary`? `@Primary` 是一个用于标记特定 Bean 定义优先级的注解。如果在一个上下文中存在多个候选 Bean,则被标注为 `@Primary` 的 Bean 将会被优先考虑作为依赖注入的目标[^1]。 #### 使用场景 假设应用程序中有两个 RabbitMQ 连接工厂分别指向不同的 RabbitMQ 实例: ```java @Configuration public class RabbitConfig { @Bean @Primary public ConnectionFactory primaryConnectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory("primary-rabbitmq-host"); return connectionFactory; } @Bean public ConnectionFactory secondaryConnectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory("secondary-rabbitmq-host"); return connectionFactory; } } ``` 在此配置中,`primaryConnectionFactory` 被标记为 `@Primary`,因此它将成为任何未显式指定目标类型的依赖注入操作中的首选项。 #### 常见问题及解决方案 1. **冲突的 Bean 注入** 如果没有正确处理多个相同类型的 Bean,在某些情况下可能导致运行时异常。通过使用 `@Qualifier` 或者更推荐的方式——`@Primary` 来消除歧义。 2. **动态切换连接工厂** 当需要根据条件动态选择不同 RabbitMQ 实例时,可以通过实现自定义逻辑来决定哪个连接工厂应该生效。例如利用 SpEL 表达式或者环境变量控制: ```java @Configuration @ConditionalOnProperty(name = "rabbitmq.use-primary", havingValue = "true") public static class PrimaryRabbitConfig { @Bean @Primary public ConnectionFactory connectionFactory() { return new CachingConnectionFactory("primary-rabbitmq-host"); } } @Configuration @ConditionalOnProperty(name = "rabbitmq.use-primary", havingValue = "false", matchIfMissing = true) public static class SecondaryRabbitConfig { @Bean @Primary public ConnectionFactory connectionFactory() { return new CachingConnectionFactory("secondary-rabbitmq-host"); } } ``` 3. **测试环境中隔离资源** 在单元测试阶段可能希望模拟真实生产环境的行为而不影响实际数据流。这时可以创建独立的测试专用组件并应用 `@Primary` 提高其优先级以便覆盖原有设置。 #### 示例代码片段 下面是一个完整的例子展示如何结合 `@Primary` 和 RabbitMQ Listener Container Factory 设置: ```java import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @Configuration public class RabbitMqConfig { @Bean @Primary public CachingConnectionFactory primaryConnectionFactory() { CachingConnectionFactory factory = new CachingConnectionFactory(); factory.setHost("localhost"); // 主队列服务器地址 return factory; } @Bean public SimpleMessageListenerContainer listenerContainer(CachingConnectionFactory connectionFactory) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); container.setQueueNames("test.queue"); return container; } } ``` 此配置文件设置了主要使用的 RabbitMQ Host 地址,并将其应用于所有的基于该类实例化的消费者端点上。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值