<>1. 公司里一个项目真实的应用,需求如下:



创建的一个user或group,需要不同人员去approve,user
或group状态就会发生变化。这个很适合使用spring-statemachine来做。以建立group来说明如下

*
建立一个group,状态是pending Approved

*
approve这个group,状态变成partial Approved

*
approve这个group,如果建立的group级别是高级,状态变成Pending Admin
Approve,需要公司人员(Admin级别)去approve. 如果group级别不是高级,状态直接变成Approved

*
公司人员(Admin级别) approve这个group,状态变成Pending Admin Approve Confirm

*
公司人员(Admin级别) approve这个group,状态变成Approved

*
第4步,如果公司人员(Admin级别) reject这个group,状态变成Pending Admin Reject Confirm

*
公司人员(Admin级别) approve这个group,状态变成Rejected

*
第7步,如果公司人员(Admin级别) reject这个group,状态变成Pending Admin Approve,重新走流程

以下为代码参考了官方sample-persist
<https://docs.spring.io/spring-statemachine/docs/2.0.3.BUILD-SNAPSHOT/reference/htmlsingle/#statemachine-examples-persist>
并做了更改

<>2.sql
CREATE TABLE `pagroup` ( `groupId` int(11) DEFAULT NULL, `groupName`
varchar(256) DEFAULT NULL, `status` int(11) DEFAULT NULL, `isAdvance`
tinyint(1) NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8
对应的Pojo
public class Group { private int groupId; private String groupName; private
int state; private boolean isAdvance; //getter and setter... }
<>3.statemahine config

spring
statemachine的Configuration,java代码如下,实际上就是用java代码描述1的那个图。刚开始写有点懵,实际写下就很简单了,就是source到target的状态变化需要用什么event(事件)去trigger。需要说明的是
withChoice(),这个就是说当状态从pending
approved改到choice时,statemahine引擎需要知道如何去选下个状态是Approved还是pending Admin
approve,这里逻辑如果group 是isadvance就是pending Admin approve否则就是approved
@Configuration @EnableStateMachine public class StateMachineConfig extends
StateMachineConfigurerAdapter<Status,ActionType>{ @Override public void
configure(StateMachineStateConfigurer<Status,ActionType> states) throws
Exception { states.withStates() .initial(Status.PENDING_APPROVAL)
.choice(Status.CHOICE) .states(EnumSet.allOf(Status.class)); } @Override public
void configure(StateMachineTransitionConfigurer<Status,ActionType> transitions)
throws Exception { transitions .withExternal()
.source(Status.PENDING_APPROVAL).target(Status.PARTIALLY_APPROVED).event(ActionType.APPROVE)
.and() .withExternal()
.source(Status.PARTIALLY_APPROVED).target(Status.CHOICE).event(ActionType.APPROVE)
.and() .withChoice() .source(Status.CHOICE)
.first(Status.PENGDING_DOCUMENT_CHECK,(context)->{ Group group =
context.getMessage().getHeaders().get("group",Group.class); return
group.isAdvance(); }) .last(Status.APPROVED) .and() .withExternal()
.source(Status.PENGDING_DOCUMENT_CHECK).target(Status.PENDING_APPROVAL_CONFIRMATION).event(ActionType.APPROVE)
.and() .withExternal()
.source(Status.PENDING_APPROVAL_CONFIRMATION).target(Status.APPROVED).event(ActionType.APPROVE)
.and() .withExternal()
.source(Status.PENDING_APPROVAL_CONFIRMATION).target(Status.PENGDING_DOCUMENT_CHECK).event(ActionType.REJECT)
.and() .withExternal()
.source(Status.PENGDING_DOCUMENT_CHECK).target(Status.PENDING_REJECT_CONFIRMATION).event(ActionType.REJECT)
.and() .withExternal()
.source(Status.PENDING_REJECT_CONFIRMATION).target(Status.PENGDING_DOCUMENT_CHECK).event(ActionType.REJECT)
.and() .withExternal()
.source(Status.PENDING_REJECT_CONFIRMATION).target(Status.REJECTED).event(ActionType.APPROVE)
.and() .withExternal()
.source(Status.PENDING_APPROVAL).target(Status.REJECTED).event(ActionType.REJECT)
.and() .withExternal()
.source(Status.PARTIALLY_APPROVED).target(Status.REJECTED).event(ActionType.REJECT);
} }
<>4.事件(ActionType)和状态(Status)定义
public enum ActionType { APPROVE(1), REJECT(2); } public enum Status {
PENDING_APPROVAL("status.pending_approval", 1),
PARTIALLY_APPROVED("status.partially_approval", 2), APPROVED("status.approved",
3), REJECTED("status.rejected", 4),
PENGDING_DOCUMENT_CHECK("status.pending_document_check", 5),
PENDING_APPROVAL_CONFIRMATION("status.pending_approval_confirmation", 10),
PENDING_REJECT_CONFIRMATION("status.pending_reject_confirmation", 11),
CHOICE("spring.state.machine.choice",501); private String msgKey; private int
statusCode; Status(String desc, int statusCode) { this.msgKey = desc;
this.statusCode = statusCode; } public static Status valueOf(int statusCode) {
Iterator<Status> iterator = EnumSet.allOf(Status.class).iterator(); while
(iterator.hasNext()){ Status st =iterator.next(); if(st.statusCode==statusCode)
return st; } throw new IllegalArgumentException("invalid status code"); }
public String getMsgKey() { return msgKey; } public int getStatusCode() {
return statusCode; } }
PersistStateMachineHandler

该代码就是往statemachine里添加一个拦截器PersistingStateChangeInterceptor,
拦截到所有的preStateChange事件,就通知CompositePersistStateChangeListener里面注册的listener去处理
@Component public class PersistStateMachineHandler extends
LifecycleObjectSupport { private final StateMachine<Status,ActionType>
stateMachine; private final PersistingStateChangeInterceptor interceptor = new
PersistingStateChangeInterceptor(); private final
CompositePersistStateChangeListener listeners = new
CompositePersistStateChangeListener(); /** * Instantiates a new persist state
machine handler. * * @param stateMachine the state machine */ @Autowired public
PersistStateMachineHandler(StateMachine<Status,ActionType> stateMachine) {
Assert.notNull(stateMachine, "State machine must be set"); this.stateMachine =
stateMachine; }
//会被LifecycleObjectSupport父类的InitializingBean.afterPropertiesSet()里调用 protected
void onInit() throws Exception {
//往stateMachine加入拦截器PersistingStateChangeInterceptor
stateMachine.getStateMachineAccessor().doWithAllRegions(new
StateMachineFunction<StateMachineAccess<Status,ActionType>>() { public void
apply(StateMachineAccess<Status,ActionType> function) {
function.addStateMachineInterceptor(interceptor); } }); //获取所有
PersistStateChangeListener的bean注册到CompositePersistStateChangeListener
Map<String, PersistStateChangeListener> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors((ListableBeanFactory)
this.getBeanFactory(), PersistStateChangeListener.class, true, false); if
(!matchingBeans.isEmpty()) { listeners.setListeners(new
ArrayList(matchingBeans.values())); } } /** * Handle event with entity. * *
@param event the event * @param state the state * @return true if event was
accepted */ public boolean handleEventWithState(Message<ActionType> event,
Status state) { stateMachine.stop();
List<StateMachineAccess<Status,ActionType>> withAllRegions =
stateMachine.getStateMachineAccessor().withAllRegions(); for
(StateMachineAccess<Status,ActionType> a : withAllRegions) {
a.resetStateMachine(new DefaultStateMachineContext<Status,ActionType>(state,
null, null, null)); } stateMachine.start(); return
stateMachine.sendEvent(event); } /** * Adds the persist state change listener.
* * @param listener the listener */ public void
addPersistStateChangeListener(PersistStateChangeListener listener) {
listeners.register(listener); } /** * The listener interface for receiving
persistStateChange events. * The class that is interested in processing a
persistStateChange * event implements this interface, and the object created *
with that class is registered with a component using the * component's
<code>addPersistStateChangeListener</code> method. When * the
persistStateChange event occurs, that object's appropriate * method is invoked.
*/ public interface PersistStateChangeListener { /** * Called when state needs
to be persisted. * * @param state the state * @param message the message *
@param transition the transition * @param stateMachine the state machine */
void onPersist(State<Status,ActionType> state, Message<ActionType> message,
Transition<Status,ActionType> transition, StateMachine<Status,ActionType>
stateMachine); } private class PersistingStateChangeInterceptor extends
StateMachineInterceptorAdapter<Status,ActionType> { @Override public void
preStateChange(State<Status, ActionType> state, Message<ActionType> message,
Transition<Status, ActionType> transition, StateMachine<Status, ActionType>
stateMachine) { listeners.onPersist(state,message,transition,stateMachine); } }
private class CompositePersistStateChangeListener extends
AbstractCompositeListener<PersistStateChangeListener> implements
PersistStateChangeListener { public void onPersist(State<Status,ActionType>
state, Message<ActionType> message, Transition<Status,ActionType> transition,
StateMachine<Status,ActionType> stateMachine) { for
(Iterator<PersistStateChangeListener> iterator = getListeners().reverse();
iterator.hasNext(); ) { PersistStateChangeListener listener = iterator.next();
listener.onPersist(state, message, transition, stateMachine); } } } }
GroupController
@RestController public class GroupController { @Autowired GroupService
groupService; @RequestMapping("/group/list") public List<Group> list(){ return
groupService.listAll(); } @PostMapping("/group/create") public boolean
create(@RequestBody Group group){ groupService.create(group); return true; }
//web入口,处理某个group{id}的某个事件,例如group/2/APPROVE就是对group id 为2 做approve
@RequestMapping("/group/{id}/{event}") public boolean
handle(@PathVariable("id")Integer id,@PathVariable("event") String event){
return groupService.handleAction(id,event); } }
GroupService
@org.springframework.stereotype.Service public class GroupService { @Autowired
private PersistStateMachineHandler handler; @Autowired private GroupRepository
repository; public boolean handleAction(int groupId, String event) { Group
group = repository.findGroupById(groupId); //发送事件去触发状态机 return
handler.handleEventWithState(MessageBuilder.withPayload(ActionType.valueOf(event))
.setHeader("group", group).build(), Status.valueOf(group.getState())); } public
void create(Group group) { repository.create(group); } public List listAll() {
return repository.listAll(); } }
GroupRepoository 相当于Dao
@Repository public class GroupRepository { @Autowired private JdbcTemplate
jdbcTemplate; public void create(Group group){ jdbcTemplate.update("insert into
pagroup(groupId,groupName,status) values (?,?,?)", group.getGroupId(),
group.getGroupName(), Status.PENDING_APPROVAL.getStatusCode()); } public List
listAll() { List list = jdbcTemplate.query("select
groupId,groupName,status,isAdvance from pagroup",rowMapper()); return list; }
public Group findGroupById(int groupId) { Group group =
jdbcTemplate.queryForObject("select groupId, groupName,status,isAdvance from
pagroup where groupId = ?", new Object[]{groupId},rowMapper()); return group; }
private RowMapper<Group> rowMapper(){ return new RowMapper<Group>() { public
Group mapRow(ResultSet rs, int rowNum) throws SQLException { Group group = new
Group(rs.getInt("groupId"), rs.getString("groupName"));
group.setState(rs.getInt("status"));
group.setAdvance(rs.getBoolean("isAdvance")); return group; } }; } }
GroupPersistStateChangeListener 监听到状态机状态更改,就更新数据库里的对应字段
@Component public class GroupPersistStateChangeListener implements
PersistStateMachineHandler.PersistStateChangeListener { @Autowired private
JdbcTemplate jdbcTemplate; public GroupPersistStateChangeListener(JdbcTemplate
jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void
onPersist(State<Status, ActionType> state, Message<ActionType> message,
Transition<Status, ActionType> transition, StateMachine<Status, ActionType>
stateMachine) { if (message != null &&
message.getHeaders().containsKey("group")) { Group group =
message.getHeaders().get("group", Group.class); jdbcTemplate.update("update
pagroup set status = ? where groupId = ?", state.getId().getStatusCode(),
group.getGroupId()); } } }
启动Spring工程
@SpringBootApplication public class BootStrap { public static void
main(String[] args) { SpringApplicationBuilder builder = new
SpringApplicationBuilder(BootStrap.class); builder.run(args); } }
代码在github
<https://github.com/rechardguo/spring-learn/tree/master/springboot-web-integration/spring-statemachine>