Spring statemachine-实际项目应用

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

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

  1. 建立一个group,状态是pending Approved

  2. approve这个group,状态变成partial Approved

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

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

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

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

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

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

以下为代码参考了官方sample-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{


    @Override
    public void configure(StateMachineStateConfigurer states) throws Exception {
        states.withStates()
                .initial(Status.PENDING_APPROVAL)
                .choice(Status.CHOICE)
                .states(EnumSet.allOf(Status.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer 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 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 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 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>() {
            public void apply(StateMachineAccess function) {
                function.addStateMachineInterceptor(interceptor);
            }
        });

        //获取所有 PersistStateChangeListener的bean注册到CompositePersistStateChangeListener
        Map 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 event, Status state) {
        stateMachine.stop();
        List> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions();
        for (StateMachineAccess a : withAllRegions) {
            a.resetStateMachine(new DefaultStateMachineContext(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 addPersistStateChangeListener 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 state, Message message, Transition transition,
                       StateMachine stateMachine);
    }

    private class PersistingStateChangeInterceptor extends StateMachineInterceptorAdapter {
        @Override
        public void preStateChange(State state, Message message, Transition transition, StateMachine stateMachine) {
            listeners.onPersist(state,message,transition,stateMachine);
        }
    }

    private class CompositePersistStateChangeListener extends AbstractCompositeListener implements
            PersistStateChangeListener {
        public void onPersist(State state, Message message,
                              Transition transition, StateMachine stateMachine) {
            for (Iterator 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 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 rowMapper(){
        return  new RowMapper() {
            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 state, Message message, Transition transition, StateMachine 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

你可能感兴趣的:(spring)