Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Axon SpringBeanParameterResolverFactory with null applicationContext when instantiated from classpath loader

I have asked this question in the Axon google group 10 days ago, but have gotten no love so far. I'm beginning to wonder if maybe it's more suitable to ask here. I am trying to inject a bean as parameter to a command handler, but can't figure out how to properly use the SpringBeanParameterResolverFactory.

Here's my aggregate config:

@Bean
public AggregateFactory<ChequeSettingsAggregate> settingsAggregateFactory() {
    return new SpringPrototypeAggregateFactory<>("settingsAggregate");
}

@Bean(name = "settingsAggregateRepository")
public Repository<SettingsAggregate> settingsAggregateRepository(
        EventStore eventStore, SnapshotTriggerDefinition snapshotTriggerDefinition) {

    return new MyEventRepository<>(
            SettingsAggregate.class,
            settingsAggregateFactory(),
            eventStore,
            snapshotTriggerDefinition);
}

MyEventRepository extends EventSourcingRepository.

Here's my service:

@Service
@Qualifier("settingsService") // Makes no difference with or without
public class SettingsService extends BaseService { ... }

Here's my aggregate:

@Aggregate(repository = "settingsAggregateRepository")
public class SettingsAggregate extends AggregateBase {
    SettingsAggregate() {
        super();
    }

    void handle(UpdateSettingsCommand cmd, @Qualifier("settingsService") SettingsService settingsService) { // Makes no difference without the qualifier.
        settingsService.findOneById(cmd.getId());

        ...
    }
...
}

I get the error:

nested exception is org.axonframework.messaging.annotation.UnsupportedHandlerException:
 Unable to resolve parameter 1 (SettingsService) in handler void 
    com.domain.settings.api.eventstore.aggregates.SettingsAggregate.handle(
        com.domain.settings.api.eventstore.commands.cheques.UpdateSettingsCommand,
        com.domain.settings.api.services.SettingsService).

The SpringBeanResolverFactory is not one of the factories in MultiParameterResolverFactory. This class' factory list is populated by ClasspathParameterResolverFactory. The factory paths resolved lead to parameter resolver factory objects being instantiated (not autowired from Spring's DI container). Sometime after this code runs, the SpringBeanResolverFactory is added as a bean TO the DI container, unlike the other factories (haven't really looked deep enough to figure what's cause that... probably auto configuration). This bean however is not being used as a resolver because, although it's now part of the application context collection of beeans, it is not added to the list of factories in MultiParameterResolverFactory. To add the SpringBeanResolverFactory resolver to the factory list, I added a org.axonframework.messaging.annotation.ParameterResolverFactory file to META-INF/services with the path to the resolver. This adds the resolver to the list of factories, but doesn't set it's applicationContext variable, needed to pull the service I want to inject as a parameter. So, when the command handler service parameter is being resolved, the SpringBeanResolverFactory returns null because it doesn't have the applicationContext field set.

So, I fail to see what the purpose of adding the SpringBeanResolverFactory to the DI container (through autoconfig presumably, which has applicationContext set btw) when it's not used to resolve parameters. I also fail to see the purpose of instantiating the SpringBeanResolverFactory from class path if the context is not going to be set.

I'm not sure what I am missing here, but I would really like to figure out how I can get this parameter injection to work.

Thanks in advance, Bruno

like image 227
Bruno Avatar asked Sep 02 '26 10:09

Bruno


1 Answers

the issue seems to be in how you create your MyEventRepository instance. You need to specify how Axon can resolve parameters if the default (Classpath based lookup of parameter resolvers) doesn't suffice. Adding the SpringBeanParameterResolver to the list of classpath resolvers doesn’t help, because it will have an instance created outside of the context of Spring, and won't be able to access the Spring ApplicationContext.

In your builder, you should pass a ParameterResolverFactory that tells the Repository how you want the aggregates loaded by that repository to be invoked. You should probably be able to autowire an instance from the application context, as Axon will register a ParameterResolver that will automatically delegate to any other ParameterResolvers defined in the application context.

An even better solution would be to let Spring Boot and Axon create everything for you. Is there a specific reason you’d want to create your own Repository subclass? Why not just keep the defaults?

A final note: Axon's parameter injection mechanism was not designed to be a full replacement of Spring's dependency injection. It doesn't, for example, support @Qualifiers or InjectionPoints.

like image 75
Allard Avatar answered Sep 05 '26 17:09

Allard



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!