简体   繁体   English

在 Spring 中以编程方式启用和禁用某些 @RabbitListener 吗?

[英]Programmatically enable and disable certain @RabbitListener's in Spring?

I have a class A that publishes event E1.我有一个发布事件 E1 的 A 类。 E1 is consumed by class B in the same application that is annotated with @RabbitListener . E1 由 B 类在使用@RabbitListener注释的同一应用程序中使用。 B does some things and then publishes event E2 that is consumed by C etc etc (forming a process chain). B 做一些事情,然后发布由 C 等消费的事件 E2(形成一个流程链)。

What I want to do is two things:我想做的是两件事:

  1. I want to test A in an integration test but while doing so I'd like to disable the RabbitListener's so that the entire process that is the result of E1 being published is not executed.我想在集成测试中测试 A,但在这样做时,我想禁用RabbitListener's以便不执行作为 E1 发布结果的整个过程。 I only want to assert that A does what it's supposed to and publishes E1.我只想断言 A 做了它应该做的事情并发布了 E1。 I have managed to accommodate this by setting spring.rabbitmq.listener.auto-startup=false .我通过设置spring.rabbitmq.listener.auto-startup=false设法适应了这一点。
  2. I also want to test B in an integration test by publishing E1 to RabbitMQ so that I can be confident that I've configured B's RabbitListerner correctly.我还想通过将 E1 发布到 RabbitMQ 来在集成测试中测试 B,这样我就可以确信我已经正确配置了 B 的RabbitListerner But again I don't want C to be called as a side-effect of E2 being published.但同样,我不希望 C 被称为 E2 发布的副作用。

I know I can probably do this using mocks but preferably I'd like to test the real deal and using the actual components (including sending the message to an actual RabbitMQ instance that in my case is running in Docker).我知道我可能可以使用模拟来做到这一点,但最好我想测试真正的交易并使用实际组件(包括将消息发送到在我的情况下在 Docker 中运行的实际 RabbitMQ 实例)。

Can I achieve this in a nice way in Spring Boot?我可以在 Spring Boot 中以一种很好的方式实现这一目标吗? Or is it perhaps recommended to use @RabbitListenerTest and indeed use mocks?或者是否建议使用@RabbitListenerTest并确实使用@RabbitListenerTest

The @RabbitListener has id property: @RabbitListenerid属性:

/**
 * The unique identifier of the container managing for this endpoint.
 * <p>If none is specified an auto-generated one is provided.
 * @return the {@code id} for the container managing for this endpoint.
 * @see org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry#getListenerContainer(String)
 */
String id() default "";

And that RabbitListenerEndpointRegistry#getListenerContainer(String) returns MessageListenerContainer and there you already can control start()/stop() of individual @RabbitListener handler.并且RabbitListenerEndpointRegistry#getListenerContainer(String)返回MessageListenerContainer并且您已经可以控制单个@RabbitListener处理程序的start()/stop()

A follow-up idea to the accepted answer is to have some sort of abstract BaseAmqpIntegrationTest which does the following:接受答案的后续想法是使用某种抽象的BaseAmqpIntegrationTest ,它执行以下操作:

public abstract class BaseAmqpIntegrationTest {

    @Autowired
    protected RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry;

    @BeforeEach
    protected void setUpBeforeEach() {
        rabbitListenerEndpointRegistry.getListenerContainers()
                                      .forEach(Lifecycle::stop);

        getRequiredListenersToStart().forEach(listener -> rabbitListenerEndpointRegistry.getListenerContainer(listener)
                                                                                        .start());
    }

    protected abstract List<String> getRequiredListenersToStart();

}

This makes it reusable and ensures that all @RabbitListener s are disabled "by default" and requires each test to explicitly enable the listener(s) that it tests.这使其可重用并确保所有@RabbitListener都“默认”禁用,并要求每个测试显式启用它测试的侦听器。 The test sub classes can then simply override getRequiredListenersToStart() to provide the IDs of the @RabbitListener s which they require.然后,测试子类可以简单地覆盖getRequiredListenersToStart()以提供它们需要的@RabbitListener的 ID。

PS: Cleaning it up would of course also work: PS:清理它当然也有效:

public abstract class BaseAmqpIntegrationTest {

    @AfterEach
    protected void cleanUpAfterEach() {
        rabbitListenerEndpointRegistry.getListenerContainers()
                                      .forEach(Lifecycle::stop);
    }

}

Or a bit more fine-grained:或者更细粒度的:

public abstract class BaseAmqpIntegrationTest {

    @AfterEach
    protected void cleanUpAfterEach() {
        getRequiredListenersToStart().forEach(listener -> rabbitListenerEndpointRegistry.getListenerContainer(listener)
                                                                                        .stop());
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM