graphql-java-kickstart / graphql-spring-boot

GraphQL and GraphiQL Spring Framework Boot Starters - Forked from oembedler/graphql-spring-boot due to inactivity.
https://www.graphql-java-kickstart.com/spring-boot/
MIT License
1.51k stars 324 forks source link

Unable to inject executionStrategies bean #336

Open petr-ujezdsky opened 4 years ago

petr-ujezdsky commented 4 years ago

TL;DR

public class GraphQLWebAutoConfiguration {

    @Autowired(required = false)
    private Map<String, ExecutionStrategy> executionStrategies;

is populated by spring as map beanName -> bean, when I have some ExecutionStrategy in spring context.

Problem

I was trying to inject my custom executionStrategies bean for ExecutionStrategyProvider creation inside GraphQLWebAutoConfiguration.

In my own @Configuration annotated class I have created simple bean

@Configuration
public class GraphqlConfiguration {

    @Bean
    public Map<String, ExecutionStrategy> executionStrategies(AsyncTransactionalExecutionStrategy strategy) {
        Map<String, ExecutionStrategy> executionStrategyMap = new HashMap<>();
        executionStrategyMap.put(QUERY_EXECUTION_STRATEGY, strategy);
        executionStrategyMap.put(MUTATION_EXECUTION_STRATEGY, new AsyncExecutionStrategy());
        return executionStrategyMap;
    }
}

Where AsyncTransactionalExecutionStrategy is another bean (with transaction over whole graphql execution to enable lazy hibernate calls)

@Component
public class AsyncTransactionalExecutionStrategy extends AsyncExecutionStrategy {

    @Override
    @Transactional
    public CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException {
        return super.execute(executionContext, parameters);
    }
}

The problem is, that the executionStrategyProvider(...) inside GraphQLWebAutoConfiguration is called before my configuration and my map bean creation.

public class GraphQLWebAutoConfiguration {

    @Autowired(required = false)
    private Map<String, ExecutionStrategy> executionStrategies;

executionStrategies is injected with map beanName -> bean (of 1 instance) instead of map from my bean that uses GraphQLWebAutoConfiguration.QUERY_EXECUTION_STRATEGY and alike as keys. The executionStrategyProvider then works unexpectedly - it uses the one strategy for query, mutation and subscription.

Solution (kind of)

The solution is to name my bean using the expected keys

@Component(QUERY_EXECUTION_STRATEGY)
public class AsyncTransactionalExecutionStrategy extends AsyncExecutionStrategy {

and do not use the executionStrategies(...) bean inside my config. This populates the executionStrategies map with proper keys (beanName = expected key). But is this the "right" way that is intended?

oliemansm commented 4 years ago

I'd say your current solution is more a workaround and a better mechanism should be provided.

szediwy commented 4 years ago

For me it worked to define an ExecutionStrategyProvider instead of defining the Map.

@Bean
public ExecutionStrategyProvider executionStrategyProvider(
        AsyncTransactionalExecutionStrategy queryExecutionStrategy) {
    return new DefaultExecutionStrategyProvider(queryExecutionStrategy, null, null);
}