RabbitMQ SimpleRabbitListenerContainerFactory Bean 등록 이슈 해결하기

Y_Sevin·2024년 5월 17일
2

문제발생 과정

RabbitMQ의 환경분리를 위해 host를 추가하던 중 아래와 같이 Bean 등록 이슈가 발생했다.

Parameter 1 of method simpleRabbitListenerContainerFactory in org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration required a single bean, but 2 were found:
	- defaultConnectionFactory: defined by method 'defaultConnectionFactory' in class path resource [com/example/hairbackend/config/RabbitmqConfig.class]
	- updateConnectionFactory: defined by method 'updateConnectionFactory' in class path resource [com/example/hairbackend/config/RabbitmqConfig.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

해당 에러를 보면 simpleRabbitListenerContainerFactory에서 빈을 등록하는 과정에 2개의 ConnectionFactory Bean이 탐색되어 우선순위를 정해달라는 문구가 나와있다.

하지만 우리 코드는 아래와 같이 구현되어 있다.

    @Bean
    public SimpleRabbitListenerContainerFactory defaultRabbitListenerContainerFactory(
            @Qualifier("defaultConnectionFactory") ConnectionFactory connectionFactory,
            MessageConverter messageConverter) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(messageConverter);
        return factory;
    }

    @Bean
    public SimpleRabbitListenerContainerFactory updateRabbitListenerContainerFactory(
            @Qualifier("updateConnectionFactory") ConnectionFactory connectionFactory,
            MessageConverter messageConverter) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(messageConverter);
        return factory;
    }

위 코드를 보면 알 수 있듯이 @Qualifier를 사용해 생성자로 초기화할 때 지정한 빈으로 초기화되도록 구현해 놓은 상태이다.. 때문에 따로 우선순위 설정을 하지 않더라도 이미 내부에서 사용할 빈을 특정했으니 위와같은 오류가 발생하면 안된다.
하지만 오류메세지에서 제안한 @Primary를 사용했을 때 오류가 해결되어서 더욱 혼란스러웠다..

해결과정 (고민 과정)

문제를 해결하기 위해 작성한 코드 외에도 RabbitMQ의 동작 방식을 자세히 살펴봐야겠다는 결론에 도달했다.

RabbitMQ의 동작 과정을 분석하던 중, rabbitListenerContainerFactory라는 이름의 빈을 기본적으로 참조하는 것을 발견했다. 필자가 등록한 빈은 defaultRabbitListenerContainerFactoryupdateRabbitListenerContainerFactory였기 때문에, 기본적으로 등록되지 않음을 알 수 있었다.

그러나 한 가지 의문이 들었다. 기본값으로 등록되지 않은 빈을 참조하려면 해당 빈을 직접 생성하지 않는 한, 빈을 찾을 수 없다는 에러가 발생해야 한다. 그러나 실제로는 그런 에러가 발생하지 않았다. 그렇다면 RabbitMQ는 내부적으로 rabbitListenerContainerFactory라는 이름의 빈을 자동으로 생성하는 것이 아닐까 하는 의심이 들었다.

이 의심을 확인하기 위해 Bean으로 등록된 빈 리스트를 확인해 보니, 실제로 rabbitListenerContainerFactory라는 이름의 빈이 존재하고 있었다. 이로써 RabbitMQ는 해당 빈을 내부적으로 생성함을 확신하게 되었다.

해결

rabbitListenerContainerFactory라는 빈이 존재한다면 이제 원인을 특정할 수 있다.
필자는 아래와 같이 @Qualifier("updateConnectionFactory") 를 통해 빈을 지정하였다.

	@Bean
    public SimpleRabbitListenerContainerFactory updateRabbitListenerContainerFactory(
            @Qualifier("updateConnectionFactory") ConnectionFactory connectionFactory,
            MessageConverter messageConverter) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(messageConverter);
        return factory;
    }

하지만 rabbitListenerContainerFactory에는 위에서 필자가 설정한 것 처럼 @Qualifier를 사용해 빈을 지정하지 않았으니 당연하게도 2개의 빈이 탐색이 될 것이다.

문제를 해결하기 위해 현재 필자가 기본값으로 사용하고자하는 Bean을 RabbitMQ에서 기본이름으로 설정한 rabbitListenerContainerFactory으로 바꿔주면 문제를 해결할 수 있다!

    @Bean(name = "rabbitListenerContainerFactory") // 이부분만 추가하면 된다!
    public SimpleRabbitListenerContainerFactory defaultRabbitListenerContainerFactory(
            @Qualifier("defaultConnectionFactory") ConnectionFactory connectionFactory,
            MessageConverter messageConverter) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(messageConverter);
        return factory;
    }

    @Bean
    public SimpleRabbitListenerContainerFactory updateRabbitListenerContainerFactory(
            @Qualifier("updateConnectionFactory") ConnectionFactory connectionFactory,
            MessageConverter messageConverter) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(messageConverter);
        return factory;
    }
profile
매일은 아니더라도 꾸준히 올리자는 마음으로 시작하는 개발블로그😎

0개의 댓글