目录
Springboot注入Handler和Listener bean的Configuration类
一、基本回顾
1、为什么要用状态机
系统状态和条件非常多、状态间切换复杂的场景,如何更好实现状态的切换:
方法1: if-else/switch方式实现,缺点是 代码可读性、可维护性差;
方式2:状态机实现。 本质上,状态机具备了多种优点,如 可读性好,可维护性高,以及容易扩展等等
2、什么是状态机
状态机(state machine)是一种行为,它指定对象在其生命周期内响应事件所经历的状态序列,以及对象对这些事件的响应
3、状态机可归纳为4个要素
即现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。
现态:现在的状态。
条件:又称为“事件”,是一个动作发生的前提。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。
4、对应Spring StateMachine的核心步骤
- 定义状态枚举
- 定义事件枚举
- 定义状态机配置,设置初始状态,以及状态与事件之间的关系
- 定义状态监听器,当状态变更时,触发方法
5、简单例子
添加maven依赖
<dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> <version>2.0.1.RELEASE</version> </dependency>
定义状态枚举和事件枚举
-
public
enum CompanyStatus {
-
-
WAIT_SUBMIT(
0,
"待企业实名"),
-
WAIT_REAL_NAME(
1,
"待个人实名"),
-
。。。
-
-
CompanyStatus(int status, String desc) {
-
this.status = status;
-
this.desc = desc;
-
}
-
}
-
-
public
enum CompanyEvents {
-
UNKNOWN_EVENT(
0,
"未知事件"),
-
UN_REAL_NAME(
1,
"个人未实名事件"),
-
REAL_NAME(
2,
"个人实名事件"),
-
...
-
-
CompanyEvents(int value, String desc) {
-
this.value = value;
-
this.desc = desc;
-
}
-
}
完成状态机的配置
包括:(1)状态机的初始状态和所有状态;(2)状态之间的转移规则
-
@Configuration
-
@EnableStateMachineFactory(name =
"companyStateMachineFactory")
-
public class CompanyStateMachineConfig extends EnumStateMachineConfigurerAdapter<CompanyStatus, CompanyEvents> {
-
-
@Override
-
public void configure(StateMachineStateConfigurer<CompanyStatus, CompanyEvents> states) throws Exception {
-
states
.withStates()
-
// 定义初始状态
-
.initial(CompanyStatus.WAIT_SUBMIT)
-
// 定义所有状态集合
-
.states(EnumSet.allOf(CompanyStatus.class));
-
}
-
-
@Override
-
public void configure(StateMachineTransitionConfigurer<CompanyStatus, CompanyEvents> transitions) throws Exception {
-
transitions
.withExternal()
-
.source(CompanyStatus.WAIT_SUBMIT)
.target(CompanyStatus.WAIT_REAL_NAME)
-
.event(CompanyEvents.UN_REAL_NAME)
-
.and()
.withExternal()
-
.source(CompanyStatus.WAIT_SUBMIT)
.target(CompanyStatus.WAIT_CHECK)
-
.event(CompanyEvents.COMPANY_SUBMITED)
-
.and()
.withExternal()
-
...
-
-
}
-
}
简单测试一下
启动状态机、发送不同的事件,通过日志验证状态机的流转过程
-
@Resource
-
StateMachine<CompanyStatus, CompanyEvents> stateMachine;
-
public void
createStateMachine(){
-
stateMachine
.start();
-
stateMachine
.sendEvent(CompanyEvents.UN_REAL_NAME);
-
stateMachine
.sendEvent(CompanyEvents.REAL_NAME);
-
}
添加Listener 监听器,当状态变更时,触发方法
-
public
interface
PersistStateChangeListener {
-
/**
-
* 当状态被持久化,调用此方法
-
*
-
* @param state
-
* @param message
-
* @param transition
-
* @param stateMachine 状态机实例
-
*/
-
void onPersist(State<CompanyStatus, CompanyEvents> state, Message<CompanyEvents> message,
-
Transition<CompanyStatus, CompanyEvents> transition, StateMachine<CompanyStatus, CompanyEvents> stateMachine);
-
}
-
-
-
@Slf4j
-
@Component("companyPersistStateChangeListener")
-
public
class
CompanyPersistStateChangeListener
implements
PersistStateChangeListener {
-
...真正实现
-
-
}
添加拦截器
不同于Listener。其可以改变状态转移链的变化。
主要在preEvent(事件预处理)、preStateChange(状态变更的前置处理)、postStateChange(状态变更的后置处理)、preTransition(转化的前置处理)、postTransition(转化的后置处理)、stateMachineError(异常处理)等执行点生效。
-
private
class PersistingStateChangeInterceptor extends StateMachineInterceptorAdapter<CompanyStatus, CompanyEvents> {
-
-
@Override
-
public void preStateChange(
State<
CompanyStatus,
CompanyEvents> state,
Message<
CompanyEvents> message,
-
Transition<
CompanyStatus,
CompanyEvents> transition,
StateMachine<
CompanyStatus,
-
CompanyEvents> stateMachine) {
-
listeners.onPersist(state, message, transition, stateMachine);
-
}
-
}
StateMachine 状态机实例
StateMachine 状态机实例,spring statemachine支持单例、工厂模式两种方式创建。
每个statemachine有一个独有的machineId用于标识machine实例;
需要注意的是statemachine实例内部存储了当前状态机等上下文相关的属性,因此这个实例不能够被多线程共享。
例如:工厂模式创建实例
定义一个基于状态机实例的Handler
为了方便扩展更多的Listener,以及管理Listeners和Interceptors。可以定义一个基于状态机实例的Handler: PersistStateMachineHandler,以及持久化实体的监听器。
Springboot注入Handler和Listener bean的Configuration类
注解方式使用
-
@Slf4j
-
@Service
-
public class TestService {
-
-
@PostConstruct
-
private void
initialize() {
-
this
.persistStateMachineHandler
.addPersistStateChangeListener(companyPersistStateChangeListener);
-
}
-
-
public
void
userToDoSomething() {
-
-
...
//userToDoSomething
-
-
persistStateMachineHandler
.handleEventWithState(MessageBuilder.
withPayload(events)
-
.
setHeader(
"company", company.
getId()).
build(), CompanyStatus.
of(status));
-
-
}
-
-
}
多个状态机共存
在实际项目中一般都会有多个状态机并发执行,比如订单,同一时刻会有不止一个订单在运行,而每个订单都有自己的订单状态机流程。想要实现多个状态机的并行执行,就需要用到builder.
6、适用场景
各种审核逻辑业务: 如 财务审核、交易业务、TOB结算业务审核、合同状态
订单支付类业务: 如 商家单据,订单会有多种状态:已下单、待支付、已支付、待发货、待收货、已完成、退款中、退款成功等等
二、测试注意
- 单个业务场景下,状态流转正常;
- 状态异常流转时兼容处理;
- 并发操作,分别流转到不同状态正常
转载:https://blog.csdn.net/wodeyijia911/article/details/128410470