I am currently working on implementing a consumer of a Azure Service Bus queue in a Spring Boot application as explained in Azure's official guide. The code that consumes the messages from the queue per se is the following:
@Bean
public Consumer<Message<String>> consume() {
return message->{
Checkpointer checkpointer = (Checkpointer) message.getHeaders().get(CHECKPOINTER);
LOGGER.info("New message received: '{}'", message.getPayload());
checkpointer.success()
.doOnSuccess(s->LOGGER.info("Message '{}' successfully checkpointed", message.getPayload()))
.doOnError(e->LOGGER.error("Error found", e))
.block();
};
}
In the link, the consumer is defined in the Main class. However, I want to have it in a dedicated class. What I observed is that the code works either if I annotate the new class with @Component
or @Configuration
.
What intrigues me is, first, why does the @Bean
function when defined inside a component (may be that SB scans for beans in all the @Component
classes, as it does for @Configuration
?). The second, and more important, is which approach is better: where should this consumer Bean live, as it is not a standard kind of Bean (ie., one that is further injected into another components, but rather one that starts functioning out of the box). The corollary of the latter is why this code just works (it consumes the messages without further ado), as the method signature does not have anything Azure specific?
The Configuration annotation is meant to annotate classes that store Bean methods: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
public @interface Configuration
Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime
Below we alse see: "@Configuration is meta-annotated with @Component, therefore @Configuration classes are candidates for component scanning"
It means that you can in theory put the bean method into class annotated with a @Component but it does not make much sense (as it is not according to convention).
If you do not want to use @Configuration I think you can define a bean by creating a class which implements Consumer<Message> interface and annotating it with @Component. Then you can add a method accept(Message message) and add your logic there. I did not check this part but I think it should work. Adding beans as classes with @Component annotations is also per convention.
About why the code works, it is simple: inside your library (spring-cloud-azure-stream, etc) there are some classes that are enabled/started based on properties in your application.properties file. These classes try to autowire a bean implementing Consumer<Message> from the Spring context, but this bean is not defined in this library, it should be defined in your code. When you add it, bean resolves and messaging can work.