Spring状态机-基本概念使用
Spring状态机
状态机基本概念
State 状态
状态机是一个状态模型,比如一个键盘,一般左侧有普通键,右侧有数字键盘,numlock可以让数字键盘处于两种不同的状态,如果它不处于活动状态,按下数字键盘键会使用箭头导航等。如果数字键盘处于活动状态,按下这些键会输入数字。本质上,键盘的数字键盘部分可以处于两种不同的状态。
Pseudo States 伪状态
伪状态是具有特殊含义的状态,比如初始状态、结束状态、条件状态、历史状态、分支状态、合并状态、入口点、出口点状态、
Guard Conditions 守卫条件
守卫条件是动态选择运行特定的动作或转换
Events 活动
事件是驱动状态机最常用的触发行为,事件也被称为“信号”。
Transitions 转换
转换是源状态和目标状态之间的关系。状态机从一种状态到另一种状态是由触发器引起的状态转换。
Triggers 触发器
触发器引发状态转换。触发器可以由事件或计时器驱动。
Actions 行为
在状态转换前中后定义一些自定义操作,动作通常可以访问状态机上下文
以订单状态为例,使用spring状态机完成订单中状态流转。
状态流转图
+---------------------------------------------------------------+
| SM |
+---------------------------------------------------------------+
| |
| +----------------+ +----------------+ |
| *-->| PLACED | | PROCESSING | |
| +----------------+ PROCESS +----------------+ SEND |
| | |---------->| |-----+ |
| +----------------+ +----------------+ | |
| | |
| | |
| +----------------+ +----------------+ | |
| | SENT | | DELIVERED | | |
| +----------------+ DELIVER +----------------+ | |
| | |<----------| |<----+ |
| +----------------+ +----------------+ |
| |
+---------------------------------------------------------------+
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hsqldb/hsqldb -->
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.7.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.statemachine/spring-statemachine-recipes-common -->
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-recipes-common</artifactId>
<version>4.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.statemachine/spring-statemachine-core -->
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>4.0.0</version>
</dependency>
订单实体类
public class Order {
int id;
String state;
public Order(int id, String state) {
this.id = id;
this.state = state;
}
@Override
public String toString() {
return "Order [id=" + id + ", state=" + state + "]";
}
}
状态机配置
@Configuration
@EnableStateMachine
public class StateMachineConfig
extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineStateConfigurer<String, String> states)
throws Exception {
states
.withStates()
.initial("PLACED",actionInit())
.state("PROCESSING",entryPro(),exitPro())
.state("SENT")
.state("DELIVERED");
}
private Action<String, String> exitPro() {
return context -> System.out.println("进入pro状态");
}
private Action<String, String> entryPro() {
return context -> System.out.println("退出pro状态");
}
private Action<String, String> actionInit() {
return context -> System.out.println("初始化状态");
}
@Override
public void configure(StateMachineTransitionConfigurer<String, String> transitions)
throws Exception {
transitions
.withExternal()
.source("PLACED").target("PROCESSING")
.event("PROCESS")
.and()
.withExternal()
.source("PROCESSING").target("SENT")
.event("SEND")
.and()
.withExternal()
.source("SENT").target("DELIVERED")
.event("DELIVER");
}
}
状态机持久化类
public class Persist {
private final PersistStateMachineHandler handler;
//tag::snippetA[]
@Autowired
private JdbcTemplate jdbcTemplate;
//end::snippetA[]
private final PersistStateChangeListener listener = new LocalPersistStateChangeListener();
public Persist(PersistStateMachineHandler handler) {
this.handler = handler;
this.handler.addPersistStateChangeListener(listener);
}
public String listDbEntries() {
List<Order> orders = jdbcTemplate.query(
"select id, state from orders",
new RowMapper<Order>() {
public Order mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Order(rs.getInt("id"), rs.getString("state"));
}
});
StringBuilder buf = new StringBuilder();
for (Order order : orders) {
buf.append(order);
buf.append("\n");
}
return buf.toString();
}
//tag::snippetB[]
public void change(int order, String event) {
Order o = jdbcTemplate.queryForObject("select id, state from orders where id = ?",
new RowMapper<Order>() {
public Order mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Order(rs.getInt("id"), rs.getString("state"));
}
}, new Object[] { order });
handler.handleEventWithStateReactively(MessageBuilder
.withPayload(event).setHeader("order", order).build(), o.state)
.subscribe();
}
//end::snippetB[]
//tag::snippetC[]
private class LocalPersistStateChangeListener implements PersistStateChangeListener {
@Override
public void onPersist(State<String, String> state, Message<String> message,
Transition<String, String> transition, StateMachine<String, String> stateMachine) {
System.out.println("dizhi"+stateMachine);
System.out.println("uuid"+stateMachine.getUuid());
if (message != null && message.getHeaders().containsKey("order")) {
Integer order = message.getHeaders().get("order", Integer.class);
jdbcTemplate.update("update orders set state = ? where id = ?", state.getId(), order);
}
}
}
//end::snippetC[]
}
状态机持久化配置
@Configuration
public class PersistHandlerConfig {
@Autowired
private StateMachine<String, String> stateMachine;
@Bean
public Persist persist() {
return new Persist(persistStateMachineHandler());
}
@Bean
public PersistStateMachineHandler persistStateMachineHandler() {
return new PersistStateMachineHandler(stateMachine);
}
}
状态转换监听
@WithStateMachine
public class OrderStateListenerImpl {
@OnTransition(source = "PLACED", target = "PROCESSING")
public void fromS1ToS2(Message<String> message) {
System.out.println("--------------"+message.toString()+"-------------");
System.out.println("订单1 从PLACED --------------- PROCESSING");
}
}
application.properties
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false
spring.datasource.username=sa
spring.datasource.password=
data.sql
insert into orders (id, state) values (1, 'PLACED');
insert into orders (id, state) values (2, 'PROCESSING');
insert into orders (id, state) values (3, 'SENT');
insert into orders (id, state) values (4, 'DELIVERED');
schema.sql
create table orders (
id int,
state varchar(256)
);
controller
@RestController
public class PersistController {
@Autowired
private Persist persist;
@GetMapping("db")
public String listDbEntries() {
return persist.listDbEntries();
}
@GetMapping("process")
public void process(@RequestParam int order) {
persist.change(order, "PROCESS");
}
@GetMapping("send")
public void send(@RequestParam int order) {
persist.change(order, "SEND");
}
@GetMapping("deliver")
public void deliver(@RequestParam int order) {
persist.change(order, "DELIVER");
}
}
License:
CC BY 4.0