Spring Statemachine 3.0.0-RC1 Released

Releases | Janne Valkealahti | December 11, 2020 | ...

Yes, we're going reactive!

On behalf of the team and everyone that contributed, I am pleased to announce that the first release candidate of Spring Statemachine 3.0.0 has been released and is available from our milestone repository.

Highlights of this release include:

  • Relevant api's working with statemachine now have reactive methods.
  • From a functional point of view this release is equivalent what's in 2.2.x, 2.3.x and 2.4.x branches apart from reactive changes.
  • We've chosen to align with recently released Spring Boot 2.4.1.

Internally everything is basically working atop of reactive concepts while we still keep old style methods around for blocking world. Plan is to see if and when it's possible to phase out from a blocking world. Now few words about new api's.

With good ol' blocking world you simple send an event and statemachine returns a boolean flag if event is accepted.

StateMachine<String, String> stateMachine;

boolean accepted = stateMachine.sendEvent("event1");

One slightly annoying thing were that you couldn't differentiate between cases where event were accepted(event caused a transition), denied(interceptor error or no possible transition) or deferred(in case machine config used deferred events). We chose to enrich what gets returned from event handling methods.

StateMachine<String, String> stateMachine;

Mono<StateMachineEventResult<String, String>> result = stateMachine


Firstly, one obvious thing as you probably guessed if being familiar with Reactor is that nothing happens until you subscribe to a returned Mono, secondly returned type within Mono is a StateMachineEventResult which contains more info about what happened with an event handling.

What about Flux, well simply pass in a flux and get a flux and handle it whatever you can do in a Reactor!

StateMachine<String, String> stateMachine;

Message<String> message1 = MessageBuilder.withPayload("event1").build();
Message<String> message2 = MessageBuilder.withPayload("event2").build();

Flux<StateMachineEventResult<String, String>> results = stateMachine
  .sendEvents(Flux.just(message1, message2));


What about actions as your main work with a statemachine happens there? Original blocking signature is:

public interface Action<S, E> {
  void execute(StateContext<S, E> context);

New ReactiveAction is simply a java's Function with types StateContext<S, E> and Mono<Void>:

public interface ReactiveAction<S, E>
  extends Function<StateContext<S, E>, Mono<Void>> {}

Which translates to

class MyAction implements ReactiveAction<String, String> {
  public Mono<Void> apply(StateContext<String, String> context) {
      return Mono.empty();

With configuration:

public class StateMachineConfig extends StateMachineConfigurerAdapter<String, String> {

  public void configure(StateMachineStateConfigurer<String, String> states)
      throws Exception {

  public void configure(StateMachineTransitionConfigurer<String, String> transitions)
      throws Exception {
        .actionFunction(context -> Mono.fromRunnable(() -> {

Or action as Function definition in a normal way:

Function<StateContext<String, String>, Mono<Void>> action =
  context -> Mono.empty();

Project Page | GitHub | Issues | Documentation

Get the Spring newsletter

Thank you!

Get ahead

VMware offers training and certification to turbo-charge your progress.

Learn more

Get support

Spring Runtime offers support and binaries for OpenJDK™, Spring, and Apache Tomcat® in one simple subscription.

Learn more

Upcoming events

Check out all the upcoming events in the Spring community.

View all