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

时间:2018-02-09 23:47:48

标签: java spring spring-boot dependency-injection

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

  

构造函数的参数1   com.example.notification.application.jms.JmsEventPublisher   需要一个bean,但发现了2个:            - queueAccountToNotification:由方法' queueAccountToNotification'定义。在类路径资源中   [COM /示例/通知/应用/ JMS / JmsQueueConfig.class]            - queueNotificationToAccount:由方法' queueNotificationToAccount'定义在类路径资源中   [COM /示例/通知/应用/ JMS / JmsQueueConfig.class]

     

动作:

     

考虑将其中一个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);
   }
}

3 个答案:

答案 0 :(得分:5)

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

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

错误的原因是由于

@AllArgsConstructor
public class JmsEventPublisher implements EventPublisher

@AllArgsConstructor将生成以下构造函数

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

Spring将尝试通过构造函数自动装配JmsEventPublisher,因为它有一个不是无参数构造函数的构造函数。但是,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;

 }

答案 1 :(得分:0)

我认为你误解了std::unique_ptr<MySQLQuery> MySQLConnection::CreateQuery(const std::string& query) { return std::make_unique<MySQLQuery>(con_,query); }
从文档中可以看出,“这个注释可以在字段或参数上用作自动装配时候选bean的限定符。

在你的情况下@Qualifier没有任何意义。

@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); }

上的@Qualifier

这并不能解决所有问题。 :)

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

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

@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; .......... } 适用于多个相同类型的bean,你需要放@Qualifier

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

@Primary

在MemberService中类似

public class JmsEventPublisher implements EventPublisher {

  final JmsTemplate jmsTemplate;
  final Destination q1; // q1 or q2
  .....
}

答案 2 :(得分:0)

final Destination destination更改为final Destination q1,它应该可以工作。我遇到了同样的问题,并且对我有用。