Getting started
what?
”, “how?
” and “why?
” questions. We start with a gentle
introduction to Spring Statemachine. We then build our
first Spring Statemachine application and discuss some
core principles as we go.
System Requirement
Spring Statemachine 4.0.0 is built and tested with JDK 8 (all artifacts have JDK 7 compatibility) and Spring Framework 6.0.14. It does not require any other dependencies outside of Spring Framework within its core system.
Other optional parts (such as Using Distributed States) have dependencies on
Zookeeper, while State Machine Examples has dependencies
on spring-shell
and spring-boot
, which pull other dependencies
beyond the framework itself. Also, the optional security and data access features have
dependencies to on Spring Security and Spring Data modules.
Modules
The following table describes the modules that are available for Spring Statemachine.
Module | Description |
---|---|
|
The core system of Spring Statemachine. |
|
Common recipes that do not require dependencies outside of the core framework. |
|
|
|
Common support module for |
|
Support module for |
|
Support module for |
|
Support module for |
|
Zookeeper integration for a distributed state machine. |
|
Support module for state machine testing. |
|
Support module for Spring Cloud Cluster. Note that Spring Cloud Cluster has been superseded by Spring Integration. |
|
Support module for UI UML modeling with Eclipse Papyrus. |
|
Support module for Spring Boot. |
|
Bill of Materials pom. |
|
Spring Boot starter. |
Using Gradle
The following listing shows a typical build.gradle
file created by choosing various settings at https://start.spring.io:
buildscript {
ext {
springBootVersion = '3.1.6'
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
ext {
springStatemachineVersion = '4.0.0'
}
dependencies {
compile('org.springframework.statemachine:spring-statemachine-starter')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports {
mavenBom "org.springframework.statemachine:spring-statemachine-bom:${springStatemachineVersion}"
}
}
Replace 0.0.1-SNAPSHOT with a version you want to use.
|
With a normal project structure, you can build this project with the following command:
# ./gradlew clean build
The expected Spring Boot-packaged fat jar would be build/libs/demo-0.0.1-SNAPSHOT.jar
.
You do not need the`libs-milestone` and libs-snapshot repositories for
production development.
|
Using Maven
The following example shows a typical pom.xml
file, which was created by choosing various options at https://start.spring.io:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>gs-statemachine</name>
<description>Demo project for Spring Statemachine</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-statemachine.version>4.0.0</spring-statemachine.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-bom</artifactId>
<version>${spring-statemachine.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
Replace 0.0.1-SNAPSHOT with a version you want to use.
|
With a normal project structure, you can build this project with the following command:
# mvn clean package
The expected Spring Boot-packaged fat-jar would be target/demo-0.0.1-SNAPSHOT.jar
.
You do not need the libs-milestone and libs-snapshot repositories for
production development.
|
Developing Your First Spring Statemachine Application
You can start by creating a simple Spring Boot Application
class
that implements CommandLineRunner
. The following example shows how to do so:
@SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Then you need to add states and events, as the following example shows:
public enum States {
SI, S1, S2
}
public enum Events {
E1, E2
}
Then you need to add state machine configuration, as the following example shows:
@Configuration
@EnableStateMachine
public class StateMachineConfig
extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override
public void configure(StateMachineConfigurationConfigurer<States, Events> config)
throws Exception {
config
.withConfiguration()
.autoStartup(true)
.listener(listener());
}
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.SI)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withExternal()
.source(States.SI).target(States.S1).event(Events.E1)
.and()
.withExternal()
.source(States.S1).target(States.S2).event(Events.E2);
}
@Bean
public StateMachineListener<States, Events> listener() {
return new StateMachineListenerAdapter<States, Events>() {
@Override
public void stateChanged(State<States, Events> from, State<States, Events> to) {
System.out.println("State change to " + to.getId());
}
};
}
}
Then you need to implement CommandLineRunner
and autowire StateMachine
.
The following example shows how to do so:
@Autowired
private StateMachine<States, Events> stateMachine;
@Override
public void run(String... args) throws Exception {
stateMachine.sendEvent(Events.E1);
stateMachine.sendEvent(Events.E2);
}
Depending on whether you build your application with Gradle
or Maven
,
you can run it by using java -jar build/libs/gs-statemachine-0.1.0.jar
or
java -jar target/gs-statemachine-0.1.0.jar
, respectively.
The result of this command should be normal Spring Boot output. However, you should also find the following lines:
State change to SI
State change to S1
State change to S2
These lines indicate that the machine you constructed is moving from one state to another, as it should.