André
André

Reputation: 12737

Axon Framework does not provide InjectionPoint for bean factory methods

I'm writing my first Axon tutorials, and I'd like to provide Logger as a bean, so I can easily mock/spy it:

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Logger logger(InjectionPoint ip) {
    return LoggerFactory.getLogger(ip.getMember().getDeclaringClass());
}

I usually inject it in constructors like this:

@RestController
@RequiredArgsConstructor
class FoodOrderingController {

    private final Logger logger;
    private final CommandGateway commandGateway;
    private final QueryGateway queryGateway;

That works fine. However, when I want to inject it in aggregate handler, like

@CommandHandler
public void handle(DeselectProductCommand command, Logger logger) throws ProductDeselectionException {

In this scenario, I get the following exception (with no stacktrace):

2020-05-22 14:23:44.263  WARN 16680 --- [nio-8080-exec-7] o.a.c.gateway.DefaultCommandGateway      : Command 'com.example.axon.foodcart.coreapi.command.DeselectProductCommand' resulted in org.springframework.beans.factory.BeanCreationException(Error creating bean with name 'logger' defined in class path resource [com/example/axon/foodcart/CoreConfig.class]: Unexpected exception during bean creation; nested exception is java.lang.IllegalStateException: No current InjectionPoint available for method 'logger' parameter 0)

Is it possible to inject a Logger bean in Axon Framework? Or do I have to go back to static loggers?

Upvotes: 1

Views: 521

Answers (1)

Steven
Steven

Reputation: 7275

Answer

The reason for that is likely how the SpringBeanParameterResolverFactory and it's SpringBeanParameterResolver are used to retrieve Spring beans to inject in message handling functions.

At the moment a message handling function like your @CommandHandler public void handle(DeselectProductCommand ...) is called. The SpringBeanParameterResolver#resolveParameterValue method will be called, which in turn calls the AutowireCapableBeanFactory#getBean(String). As you might have noticed there, it doesn't use any notion of an InjectionPoint.

Implementing this wouldn't be hard I believe. The AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor, String) method would be sufficient at a quick glance (DependencyDescriptor is an implementation of InjectionPoint). Might be worth providing an issue on Axon's GitHub for this, or even better, a PR.


Suggestions

However, I do have another suggestion to achieve the same non-functional requirement of logging without injecting it in every message handling function. To achieve this form of logging in an Axon application, you can go two ways:

  1. Use MessageHandlerInterceptors and MessageDispatchInterceptors which log upon ingestion and handling of any message. Quick solution would be to use Axon's LoggingInterceptor for this, which implements both interfaces. From there, you would register it to every message dispatching and handling component (thus the CommandBus, EventBus, all EventProcessors and the QueryBus).
  2. Provide a HandlerEnhancerDefintion which wraps every message handling function with your logging logic. Providing a Bean for this should be sufficient for Axon to pick it up and automatically wrap all @MessageHandler annotated methods.

Doing either of these means you are not obstructing the logic of handling commands, events and queries, with the rather dull logic of logging.


That's my two cents, hope it helps Andre!

Upvotes: 2

Related Questions