使用@Qualifier的@Bean声明不起作用

假设我有一个配置类(JmsQueueConfig,见下文)。 在本课程中,我想为整个应用程序配置多个队列。 对于一个队列,没有问题。 但是,当我添加第二个队列并尝试从服务(MemberService)使用其中一个队列时,Spring-boot告诉我

com.example.notification.application.jms.JmsEventPublisher中构造函数的参数1需要一个bean,但是找到了2个: – queueAccountToNotification:由类路径资源中的方法’queueAccountToNotification’定义[com / example / notification / application / jms / JmsQueueConfig.class] – queueNotificationToAccount:由类路径资源[com / example / notification / application / jms / JmsQueueConfig.class]中的方法’queueNotificationToAccount’定义

行动:

考虑将其中一个bean标记为@Primary,更新使用者以接受多个bean,或使用@Qualifier标识应该使用的bean

这是我的Config-Class:

@Configuration @EnableJms @ImportAutoConfiguration(classes = { JmsAutoConfiguration.class, ActiveMQAutoConfiguration.class }) public class JmsQueueConfig { @Value("${APP_QUEUE_ACCOUNT_TO_NOTIFICATION}") private String queueAccountToNotificationName; @Value("${APP_QUEUE_NOTIFICATION_TO_ACCOUNT}") private String queueNotificationNameToAccount; @Bean @Qualifier("q1") public Queue queueAccountToNotification() { return new ActiveMQQueue(queueAccountToNotificationName); } @Bean @Qualifier("q2") public Queue queueNotificationToAccount() { return new ActiveMQQueue(queueNotificationNameToAccount); } @Bean public MessageConverter jacksonJmsMessageConverter() { MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); converter.setTargetType(MessageType.TEXT); converter.setTypeIdPropertyName("_type"); return converter; } @Bean @Qualifier("p1") public EventPublisher eventPublisher(JmsTemplate jmsTemplate) { return new JmsEventPublisher(jmsTemplate, new ActiveMQQueue(queueAccountToNotificationName)); } @Bean public MessageConverter messageConverter() { return new JmsMessageConverter(); } } 

我的服务:

 @Service @FieldDefaults(level = AccessLevel.PRIVATE) @AllArgsConstructor @Slf4j public class MemberService { @Autowired @Qualifier("q1") Queue q; @Qualifier("p1") EventPublisher eventPublisher; public void createMemberSubscription(final Member member) { final MembershipSubscriptionEvent event = new MembershipSubscriptionEvent(UUID.randomUUID().toString(), member.getEmail()); //eventPublisher.publish(event); log.info("createMemberSubscription"); } public void removeMemberSubscription(final Member member) { final MembershipRemovalEvent event = new MembershipRemovalEvent(UUID.randomUUID().toString()); //eventPublisher.publish(event); log.info("removeMemberSubscription"); } } 

我是Spring生态系统的新手,我可能不太了解@Autowired和绑定。 任何好的文档或示例都将非常感激。 在Spring和SoF上,我还没有找到任何这样的文档。

更新: JmsEventPublisher类

 @Component @FieldDefaults(level = AccessLevel.PRIVATE) @Slf4j @AllArgsConstructor public class JmsEventPublisher implements EventPublisher { final JmsTemplate jmsTemplate; final Destination destination; @Override public void publish(DomainEvent event) { jmsTemplate.convertAndSend(destination, event); log.trace("Sent event. [destination={}, event={}]", destination, event); } } 

我觉得你误解了@Qualifier
从文档中可以看出 ,“ 这个注释可以在字段或参数上用作自动assembly时候选bean的限定符。

在你的情况下@Qualifier毫无意义。

 @Bean @Qualifier("q1") public Queue queueAccountToNotification() { return new ActiveMQQueue(queueAccountToNotificationName); } 

相反,你应该这样做

 @Bean(name = "q1") public Queue queueAccountToNotification() { return new ActiveMQQueue(queueAccountToNotificationName); } @Bean(name = "q2") public Queue queueNotificationToAccount() { return new ActiveMQQueue(queueNotificationNameToAccount); } 

同样在eventPublisher(...)上删除@Qualifier

这并不能解决所有问题。 🙂

如exception所示,spring无法在JmsEventPublisher中自动assemblyDestination字段。 因为它有两个Destination类型的bean(q1和q2)。
要解决这个问题,你可以做的就是。

@Primary放在其中一个bean声明中,然后使用@Qualifier

 @Primary @Bean(name = "q1") public Queue queueAccountToNotification() { return new ActiveMQQueue(queueAccountToNotificationName); } public class JmsEventPublisher implements EventPublisher { final JmsTemplate jmsTemplate; @Qualifier("q1") final Destination destination; .......... } 

底线是@Qualifier在多个相同类型的bean的情况下工作,你需要放@Primary

另一种选择是使用@Primary而不是使用@Primary,您可以将变量命名为Bean名称,然后spring将自动为您注入正确的bean。 即

 public class JmsEventPublisher implements EventPublisher { final JmsTemplate jmsTemplate; final Destination q1; // q1 or q2 ..... } 

类似于MemberService

 public class MemberService { @Autowired Queue q1; // q1 or q2 ..... } 

要更正已接受的答案,您对@Qualifier使用的理解是正确的。 它可以在两种情况下使用。 它可以与@Bean配置方法一起使用,为bean提供限定符。 如果未提供,则默认为bean名称。

它也可以用于注射目标,即使用@Autowired或@Inject注释的方法或字段。 在此上下文中,如果找到多个bean,它将帮助Spring自动assembly基础结构根据限定符(随@Bean方法提供)过滤与注入目标匹配的bean候选项

错误的原因是由于

 @AllArgsConstructor public class JmsEventPublisher implements EventPublisher 

@AllArgsConstructor将生成以下构造函数

 public JmsEventPublisher(JmsTemplate jmsTemplate, Destination destination){ //body } 

Spring将尝试通过构造函数自动assemblyJmsEventPublisher,因为它有一个不是无参数构造函数的构造函数。 但是,Destination类型的参数匹配两个Queue类型的bean。

解决方案是使用显式构造函数。 即删除@AllArgsConstructor并定义constrctor,如下所示

 public JmsEventPublisher(JmsTemplate jmsTemplate, @Qualifier("q1")Destination destination){ //body } 

或者使用字段或setter注入,即删除@AllArgsConstructor并注入字段或setter方法

 public class JmsEventPublisher implements EventPublisher { private JmsTemplate jmsTemplate; @Qualifier("q1") private Destination destination; }