The Spring StateMachine framework is probably relatively new to most developers who use Spring, and is only a little over a year old now. Its main function is to help developers simplify the development process of the state machine and make the structure of the state machine more hierarchical. It just released its third Release 1.2.0 a few days ago, which added automatic configuration of Spring Boot. Since I have been writing the Spring Boot tutorial, I simply included this content, hoping to be of some help to those who need it.
Quick start
In the same style as before, let’s use a simple example to get a feel for the Spring StateMachine. Suppose we need to implement an order related process, including order creation, order payment, order receipt three actions.
Here we will introduce the whole implementation process in detail:
-
Create a Spring Boot infrastructure project and add the spring-Statemachine-core dependency to pop.xml as follows:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.7.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> < version > 1.2.0. RELEASE < / version > < / dependency > < / dependencies >Copy the code
-
Define state and event enumerations based on the order requirements scenario described above, as follows:
New Jersey {New Jersey, New Jersey {New Jersey, New Jersey {New Jersey, New Jersey {New Jersey, New Jersey {New Jersey, New Jersey, New Jersey} // pay RECEIVE //Copy the code
It has three status (wait to PAY, Wait to receive, end) and two events that cause status migration (PAY, receive). The new status changes to Wait for Payment state up to WAITING_FOR_RECEIVE. The RECEIVE event triggers a transition from WAITING_FOR_RECEIVE to the finished DONE state.
-
Create a state machine configuration class:
@Configuration @EnableStateMachine public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.UNPAID) .states(EnumSet.allOf(States.class)); } @Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.UNPAID).target(States.WAITING_FOR_RECEIVE) .event(Events.PAY) .and() .withExternal() .source(States.WAITING_FOR_RECEIVE).target(States.DONE) .event(Events.RECEIVE); } @Override public void configure(StateMachineConfigurationConfigurer<States, Events> config) throws Exception { config .withConfiguration() .listener(listener()); } @Bean public StateMachineListener<States, Events> listener() { return new StateMachineListenerAdapter<States, Events>() { @Override public void transition(Transition<States, Events> transition) {if(transmission.gettarget ().getid () == states.new jersey) {logger.info(" order created, pending payment "); return; } if(transition.getSource().getId() == States.UNPAID && transition.getTarget().getId() == States.WAITING_FOR_RECEIVE) { Logger. info(" User completes payment, waiting for delivery "); return; } if(transition.getSource().getId() == States.WAITING_FOR_RECEIVE && transition.getTarget().getId() == States.DONE) { Logger. info(" Customer received goods, order completed "); return; }}}; }}Copy the code
There are many configurations defined in this class, which are described below:
-
The @enablestatemachine annotation is used to enable the Spring StateMachine StateMachine function
-
The configure (StateMachineStateConfigurer < States, Events > States) method is used to initialize the current state, the state machine with which the initial (States. UNPAID) defines the initial state is UNPAID, States (enumset.allof (states.class)) specifies that all states defined in the previous step be used as state definitions for the state machine.
@Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {// Define the state in the state machine states.withstates ().initial(states.permits) // Initial state .states(EnumSet.allOf(States.class)); }Copy the code
-
The configure (StateMachineTransitionConfigurer < States, Events > transitions) method is used to initialize the current what state transition state machine movement, which named we are easy to understand each a move action, There are source states, target states, and trigger events.
@Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(states.status).target(states.waiting_for_receive)// Specify state source and target.event (events.pay)// Specify trigger event.and () .withExternal() .source(States.WAITING_FOR_RECEIVE).target(States.DONE) .event(Events.RECEIVE); }Copy the code
-
Configure (StateMachineConfigurationConfigurer < States, Events > config) method for the current state of the machine state specifies the listener, the listener () is called the next content to create instances of the listener, Used to handle state transition events that occur individually.
@Override public void configure(StateMachineConfigurationConfigurer<States, Events> config) throws Exception { config .withConfiguration() .listener(listener()); // Specify the state machine's processing listener}Copy the code
-
The StateMachineListener
,>
Listener () method creates an instance of the StateMachineListener state listener. The StateMachineListener state listener() method creates an instance of the StateMachineListener state listener. The StateMachineListener state listener() method creates an instance of the StateMachineListener state listener. The actual business scenario will have more responsible logic, so we can usually put the definition of this instance into a separate class definition and load it in as an injection.
-
-
Create the application main class to complete the process:
@SpringBootApplication public class Application implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Autowired private StateMachine<States, Events> stateMachine; @Override public void run(String... args) throws Exception { stateMachine.start(); stateMachine.sendEvent(Events.PAY); stateMachine.sendEvent(Events.RECEIVE); }}Copy the code
In the run function, we define the process of the whole process, where start() is to create the order process, according to the previous definition, the order will be in the state to be paid, and then execute the payment operation by calling sendEvent(events.pay). Finally, use sendEvent(events.receive) to complete the receiving operation. After running the above program, we can get output from the console similar to the following:
INFO 2312 --- [ main] eConfig? EnhancerBySpringCGLIB? A05acb3d: order creation, wait for payment INFO - 2312 [main] O.S.S.S upport. LifecycleObjectSupport: started org.springframework.statemachine.support.DefaultStateMachineExecutor@1d2290ce INFO 2312 --- [ main] o.s.s.support.LifecycleObjectSupport : started DONE UNPAID WAITING_FOR_RECEIVE / UNPAID / uuid=c65ec0aa-59f9-4ffb-a1eb-88ec902369b2 / id=null INFO 2312 --- [ main] eConfig? EnhancerBySpringCGLIB? A05acb3d: User completes payment, waiting for delivery INFO 2312 -- [main] eConfig? EnhancerBySpringCGLIB? A05acb3d: The user has received the goods, the order is completedCopy the code
This includes handling of individual state transitions in the state listener.
From the above example, we can summarize how to use Spring StateMachine:
- Define state and event enumerations
- Define all states used for the state machine as well as the initial state
- Define the state migration action for the state machine
- Specifies the listening handler for the state machine
Status listener
With the introductory example above and the final summary, we can see that the code logic becomes very simple and hierarchical when implementing a StateMachine using Spring StateMachine. The whole state scheduling logic mainly depends on the definition of configuration mode, and all business logic operations are defined in the state listener. Actually, the state listener can achieve more functions than what we described above, it also has more event capture. We can see all of its event definitions by looking at the StateMachineListener interface:
public interface StateMachineListener<S,E> {
void stateChanged(State<S,E> from, State<S,E> to);
void stateEntered(State<S,E> state);
void stateExited(State<S,E> state);
void eventNotAccepted(Message<E> event);
void transition(Transition<S, E> transition);
void transitionStarted(Transition<S, E> transition);
void transitionEnded(Transition<S, E> transition);
void stateMachineStarted(StateMachine<S, E> stateMachine);
void stateMachineStopped(StateMachine<S, E> stateMachine);
void stateMachineError(StateMachine<S, E> stateMachine, Exception exception);
void extendedStateChanged(Object key, Object value);
void stateContext(StateContext<S, E> stateContext);
}Copy the code
Annotation listener
Spring StateMachine also provides an elegant annotation configuration implementation for state listeners. All events defined in the StateMachineListener interface can be configured using annotations. For example, we can further simplify the previously implemented state listener with annotation configuration:
@WithStateMachine public class EventConfig { private Logger logger = LoggerFactory.getLogger(getClass()); @onTransition (target = "New Jersey ") public void create() {New Jersey (New Jersey, New Jersey, New Jersey); } @onTransition (source = "New Jersey ", target =" New Jersey ") public void pay() {New Jersey = New Jersey, New Jersey = New Jersey;} @onTransition (source = "New Jersey ", New Jersey =" New Jersey ") public void pay() {New Jersey = New Jersey; } @ontransition (source = "WAITING_FOR_RECEIVE", target = "DONE") public void receive() {logger.info(" user received, order completed "); }}Copy the code
The code above implements the same functionality as the listener() method defined in Quick Start, but is more concise and readable because it is configured with annotations that dispense with the ifs of the original event listener.
Complete example for this article:
- Source: China git.oschina.net/didispace/S…
- GitHub:github.com/dyc87112/Sp…