1

TLDR: I want my Spring Boot application to run some initialization code when it starts. The code needs access to Spring beans and values.

I'm writing a Spring Boot application that will consume multiple messages from a queue concurrently. In order to do this, it needs to instantiate multiple consumer objects. Does Spring have a good way to instantiate a configurable number of instances of the same class?

The queue client I have to use acts as a thread pool. It creates one thread for every consumer object I give it. The consumer objects only receive one message at a time, and they have to fully process and acknowledge the message before they can receive another one. The consumers aren't thread-safe, so I can't just use a singleton instance.

I considered the approach below, but it doesn't feel right to me. It seems like an abuse of the @Component annotation because the Initializer instance isn't used after it's constructed. What's a better way to do it?

@Component
public class Initializer {

    public Initializer(ConsumerRegistry registry, @Value("${consumerCount}") int consumerCount) {
        for (int i = 0; i < consumerCount; i++) {
            // Each registered consumer results in a thread that consumes messages.
            // Incoming messages will be delivered to any consumer thread that's not busy.
            registry.registerConsumer(new Consumer());
        }
    }

}
mrog
  • 1,930
  • 3
  • 21
  • 28
  • What do you want to acheive with this – Naveen Kulkarni Oct 18 '19 at 05:10
  • Possible duplicate of [How does one manage object pooling in Spring?](https://stackoverflow.com/questions/893041/how-does-one-manage-object-pooling-in-spring) – Kartik Oct 18 '19 at 05:10
  • 1
    maybe thread pool? – Mateusz Oct 18 '19 at 05:12
  • I added more details to the question. – mrog Oct 18 '19 at 05:25
  • messages from a queue concurrently? Which Queue are you using, rabbitmq, activemq or kafka Streams? – Naveen Kulkarni Oct 18 '19 at 06:06
  • @NaveenKulkarni I'm using a custom wrapper around the rabbitmq client. But, that doesn't really matter. The point is that I have a specific way that I have to receive messages from the queue, and that requires creating multiple consumer instances when the Spring Boot app starts. – mrog Oct 18 '19 at 16:06
  • Would a hook into main work? E.g. https://dzone.com/articles/the-springbootapplication-annotation-example-in-ja – Janus Varmarken Oct 18 '19 at 17:09
  • https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-command-line-runner – JB Nizet Oct 18 '19 at 17:22
  • @JBNizet That's a little bit better than the approach I was using. Thanks! Would you mind submitting it as an answer? – mrog Oct 18 '19 at 19:31

1 Answers1

1

An ApplicationListener would fit your need. It gets notified on the registered event e.g. when the ApplicationContext is ready. You will have full access to all your Beans and injection.

@Component
public class StartupApplicationListener implements ApplicationListener<ApplicationReadyEvent> {

    @Inject
    private ConsumerRegistry registry;

    @Inject
    @Value("${consumerCount}")
    private int consumerCount;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        //do your logic
        for (int i = 0; i < consumerCount; i++) {
            // Each registered consumer results in a thread that consumes messages.
            // Incoming messages will be delivered to any consumer thread that's not busy.
            registry.registerConsumer(new Consumer());
        }
    }
}
Simulant
  • 19,190
  • 8
  • 63
  • 98