Correct way of injecting a repository or service into Spring statemachine Action and Guard

801 views Asked by At

I've been getting into Spring Statemachine and modelling the state of an Order with it. I read about Guards and Actions, and based on our requirements, a question came up.

What is the correct way of injecting a Spring @Repository or @Service into a Guard or Action?

As described in the docs, Guards and Actions are configured by declaring a @Bean, but I see no way of injecting a @Service or @Repository this way.

For example, take this EnumStateMachineConfigurerAdapter as an example:

@Configuration
@EnableStateMachineFactory
public class OrderStateMachineConfiguration extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {

    @Override
    public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
        states
                .withStates()
                .initial(OrderStates.PENDING)
                .initial(OrderStates.APPROVED)
                .initial(OrderStates.PAYMENT_REJECTED)
                .end(OrderStates.DELIVERED)
                .end(OrderStates.CANCELLED)
                .end(OrderStates.INVALID)
                .end(OrderStates.REFUNDED)
                .end(OrderStates.ARCHIVED)
                .states(EnumSet.allOf(OrderStates.class));

    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<OrderStates, OrderEvents> config) throws Exception {
        config
                .withConfiguration()
                .listener(new StateMachineListener());
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
        transitions
                .withExternal().source(OrderStates.PENDING).target(OrderStates.APPROVED).event(OrderEvents.APPROVE).and()
                ... more transitions
    }

    @Bean
    public Guard<OrderStates, OrderEvents> orderIsConsistent() {
        return ctx -> {
            Order order = ctx.getExtendedState().get(ORDER_KEY, Order.class);
            return order.getInconsistencies().isEmpty();
        };
    }
}

It doesn't seem right to @Autowired a service on top because this is a @Configuration class, not to mention the risk of circular references.

Another solution I came up with is maybe injecting the needed service into the extendedState or a state machine header upon the creation of the state machine and then accessing it via the StateContext?

I'd appreciate some insight on this because I couldn't find an answer in the docs.

1

There are 1 answers

0
Dauren D On

You can inject dependencies on method level:

@Bean
@Autowired
public Guard<OrderStates, OrderEvents> orderIsConsistent(OrderService orderService) {
    return ctx -> {
        Long orderId = ctx.getExtendedState().get(ORDER_ID, Long.class);
        Order order = orderService.findById(orderId);
        return order.getInconsistencies().isEmpty();
    };
}