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, Guard
s and Action
s 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.
You can inject dependencies on method level: