spring 狀態機


前言:“狀態機”見名知意,用狀態去管理業務操作,打個比方:0~1歲(出生狀態),1~3歲(認知狀態),3~6歲(啟蒙狀態),6~22歲(學習狀態),22~60(工作狀態),60以后(退休狀態),那么人一生成長經歷則是(狀態跳轉):出生狀態  -> 認知狀態  -> 啟蒙狀態  -> 學習狀態 ->  工作狀態  -> 退休狀態.

在每個狀態中都會有不同的經歷(事件),每個年齡就去干每個年齡的事情,背負這個年齡應該背負的責任,同時也享有這個年齡相應的樂趣(不同的狀態去做不同的事情),直到離開這個世界(狀態銷毀)。

人的一生不可以倒退,但是:狀態機可以,它可以在每個狀態間互相跳轉去做不同的事情,這樣的好處:邏輯清晰、可以適當的控制並發、使整個事物更加通暢,好了,上代碼:

1.新建狀態機的輔助類:因為spring內部在redis中維護了一個狀態機的hash表,所以必須接入redis

/*
 * o(-"-)o
 *
 * CopyRight(C) 2011 GameRoot Inc.
 *
 *
 *
 */
package com.qty.arena.helper.match.room;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.service.StateMachineService;
import org.springframework.stereotype.Component;

import com.qty.arena.core.ObjectReference;
import com.qty.arena.room.match.statemachine.RoomMatchEvent;
import com.qty.arena.room.match.statemachine.RoomMatchState;

/**
 * 隊伍狀態機持久化輔助類
 * 
 *
 * @data 2018年1月31日 下午2:22:58
 */
@Component
public class RoomMatchStateMachineHelper {

//    private static final Logger LOGGER = LoggerFactory.getLogger(TeamStateMachineHelper.class);

    private static final ObjectReference<RoomMatchStateMachineHelper> ref = new ObjectReference<RoomMatchStateMachineHelper>();
    
    /** 房間狀態機參數傳遞通用DTO */
    public static final String ROOM_ACTION_DELIVERY_DTO = "room_action_delivery_dto";


//    @Autowired
//    private StateMachinePersist<RoomState, RoomEvent, String> stateMachinePersist;

    @Autowired
    private StateMachineService<RoomMatchState, RoomMatchEvent> roomMatchStateMachineService;

    @Autowired  
    private RedisTemplate<String, String> redisTemplate;
    
    @PostConstruct
    void init() {
        ref.set(this);
    }

    public static RoomMatchStateMachineHelper getInstance() {
        return ref.get();
    }
    
    /**
     * 獲取狀態機
     * 
     * @param machineId
     *            狀態機編號
     * @return
     */
    public StateMachine<RoomMatchState, RoomMatchEvent> getStateMachine(String machineId) {
        return roomMatchStateMachineService.acquireStateMachine(machineId);
    }

//    /**
//     * 存儲狀態
//     * 
//     * @param machineId
//     *            狀態機編號
//     * @throws Exception
//     */
//    public void save(String machineId) throws Exception {
//        StateMachineContext<RoomState, RoomEvent> stateMachineContext = stateMachinePersist.read(machineId);
//        stateMachinePersist.write(stateMachineContext, machineId);
//    }

    /**
     * 刪除狀態機
     * 
     * @param machineId
     *            狀態機編號
     */
    public void delete(String machineId) {
        roomMatchStateMachineService.releaseStateMachine(machineId);
        redisTemplate.delete("RedisRepositoryStateMachine:" + machineId);
    }

    /**
     * 普通狀態轉換事件
     * 
     * @param machineId
     *            狀態機編號
     * @param event
     *            事件
     */
    public StateMachine<RoomMatchState, RoomMatchEvent> sendEvent(String machineId, RoomMatchEvent event) {
        StateMachine<RoomMatchState, RoomMatchEvent> stateMachine = getStateMachine(machineId);
        if (stateMachine.sendEvent(event)) {
            return stateMachine;
        }
        return null;
    }

    /**
     * 傳參的狀態轉換事件
     * 
     * @param machineId
     *            狀態機編號
     * @param event
     *            事件
     * @param headerName
     *            傳遞參數的Key           
     * @param object
     *            傳遞的參數:對象
     */
    public StateMachine<RoomMatchState, RoomMatchEvent> sendEvent(String machineId, RoomMatchEvent event, String headerName, Object object) {
        StateMachine<RoomMatchState, RoomMatchEvent> stateMachine = getStateMachine(machineId);
        Message<RoomMatchEvent> message = MessageBuilder
                .withPayload(event)
                .setHeader(headerName, object)
                .build();
        //傳遞參數的事件
        if (stateMachine.sendEvent(message)) {
            return stateMachine;
        }
        return null;
    }
}

2.配置適配器

/*
 * o(-"-)o
 *
 * CopyRight(C) 2011 GameRoot Inc.
 *
 */
package com.qty.arena.room.custom.statemachine;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.data.redis.RedisPersistingStateMachineInterceptor;
import org.springframework.statemachine.data.redis.RedisStateMachineRepository;
import org.springframework.statemachine.persist.StateMachineRuntimePersister;
import org.springframework.statemachine.service.DefaultStateMachineService;
import org.springframework.statemachine.service.StateMachineService;

import com.qty.arena.room.custom.statemachine.action.RoomCustomAlreadyDestroyEntryAction;
import com.qty.arena.room.custom.statemachine.action.RoomCustomAlreadySettlementEntryAction;
import com.qty.arena.room.custom.statemachine.action.RoomCustomCreateEntryAction;
import com.qty.arena.room.custom.statemachine.action.RoomCustomCreateLolInEntryAction;
import com.qty.arena.room.custom.statemachine.action.RoomCustomStartedEntryAction;
import com.qty.arena.room.custom.statemachine.action.RoomCustomVoteInEntryAction;

/**
 * 房間有限狀態機適配器
 * 
 *
 * @data 2018年1月27日 上午10:30:42
 */
@EnableStateMachineFactory(name = "roomCustomStateMachineFactory")
public class RoomCustomStateMachineConfig extends EnumStateMachineConfigurerAdapter<RoomCustomState, RoomCustomEvent> {

    @Autowired
    private RedisStateMachineRepository redisStateMachineRepository;

    @Autowired
    private RoomCustomCreateEntryAction roomCustomCreateEntryAction;

    @Autowired
    private RoomCustomCreateLolInEntryAction roomCustomCreateLolInEntryAction;

    @Autowired
    private RoomCustomStartedEntryAction roomCustomStartedEntryAction;

    @Autowired
    private RoomCustomVoteInEntryAction roomCustomVoteInEntryAction;

    @Autowired
    private RoomCustomAlreadyDestroyEntryAction roomCustomAlreadyDestroyEntryAction;
    
    @Autowired
    private RoomCustomAlreadySettlementEntryAction roomCustomAlreadySettlementEntryAction;

    @Bean("roomCustomStateMachinePersist")
    public StateMachineRuntimePersister<RoomCustomState, RoomCustomEvent, String> stateMachinePersist() {
        return new RedisPersistingStateMachineInterceptor<RoomCustomState, RoomCustomEvent, String>(
                redisStateMachineRepository);
    }

    @Bean("roomCustomStateMachineService")
    public StateMachineService<RoomCustomState, RoomCustomEvent> stateMachineService(
            StateMachineFactory<RoomCustomState, RoomCustomEvent> stateMachineFactory) {
        return new DefaultStateMachineService<RoomCustomState, RoomCustomEvent>(stateMachineFactory,
                stateMachinePersist());
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<RoomCustomState, RoomCustomEvent> config)
            throws Exception {
        config.withPersistence().runtimePersister(stateMachinePersist());

        config.withMonitoring().monitor(new RoomCustomStateMachineMonitor());
    }

    @Override
    public void configure(StateMachineStateConfigurer<RoomCustomState, RoomCustomEvent> states) throws Exception {
        states.withStates()
            // 定義初始狀態
            .initial(RoomCustomState.UNCREATED)
            // 定義狀態機狀態

            /** 已創建db房間狀態 */
            .state(RoomCustomState.CREATED_DB_STATE, roomCustomCreateEntryAction, null)

            /** 創建Lol房間中狀態 */
            .state(RoomCustomState.CREATE_LOL_IN, roomCustomCreateLolInEntryAction, null)

            /** 已開局狀態 */
            .state(RoomCustomState.STARTED, roomCustomStartedEntryAction, null)

            /** 投票中狀態 */
            .state(RoomCustomState.VOTE_IN, roomCustomVoteInEntryAction, null)
            
            /** 自定義房間已結算狀態  */
            .state(RoomCustomState.ALREADY_SETTLEMENT, roomCustomAlreadySettlementEntryAction, null)

            /** 房間已銷毀 */
            .state(RoomCustomState.ALREADY_DESTROY, roomCustomAlreadyDestroyEntryAction, null);

        // .states(EnumSet.allOf(RoomState.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<RoomCustomState, RoomCustomEvent> transitions)
            throws Exception {

        transitions

            // 初始化狀態 -> 已創建 = 創建房間
            .withExternal()
            .source(RoomCustomState.UNCREATED)
            .target(RoomCustomState.CREATED_DB_STATE)
            .event(RoomCustomEvent.CREATE)
            .and()
            
            // 已創建 -> 已銷毀 = 退出(最后一人)
            .withExternal()
            .source(RoomCustomState.CREATED_DB_STATE)
            .target(RoomCustomState.ALREADY_DESTROY)
            .event(RoomCustomEvent.DESTROY_ROOM_CUSTOM)
            .and()

            // 已創建 -> 已准備 = 全部准備
            .withExternal()
            .source(RoomCustomState.CREATED_DB_STATE)
            .target(RoomCustomState.CREATE_LOL_IN)
            .event(RoomCustomEvent.PREPARED_ALL)
            .and()

            // 創建Lol房間中狀態 -> 已創建 = 創建lol房間定時任務3分鍾
            .withExternal()
            .source(RoomCustomState.CREATE_LOL_IN)
            .target(RoomCustomState.CREATED_DB_STATE)
            .event(RoomCustomEvent.CREATE_LOL_ROOM_TASK)
            .and()

            // 創建lol房間中-> 已創建 = 退出
            .withExternal()
            .source(RoomCustomState.CREATE_LOL_IN)
            .target(RoomCustomState.CREATED_DB_STATE)
            .event(RoomCustomEvent.SIGN_OUT)
            .and()
            
            // 創建lol房間中 -> 已開局 = 已創建LOL房間
            .withExternal()
            .source(RoomCustomState.CREATE_LOL_IN)
            .target(RoomCustomState.STARTED)
            .event(RoomCustomEvent.GAME_ROOM_HAS_BEEN_CREATED)
            .and()
            
            // 已開局 -> 投票中 = 開始投票(6分鍾)
            .withExternal()
            .source(RoomCustomState.STARTED)
            .target(RoomCustomState.VOTE_IN)
            .event(RoomCustomEvent.START_VOTE)
            .and()
            
            // 投票中 -> 已創建 = 全部投票
            .withExternal()
            .source(RoomCustomState.VOTE_IN)
            .target(RoomCustomState.CREATED_DB_STATE)
            .event(RoomCustomEvent.ALL_VOTE)
            .and()
            
            // 投票中 -> 已創建 = 全部投票
            .withExternal()
            .source(RoomCustomState.STARTED)
            .target(RoomCustomState.CREATED_DB_STATE)
            .event(RoomCustomEvent.ALL_VOTE)
            .and()
            
            //投票中 -> 已創建 = 結算延時任務(2小時)
            .withExternal()
            .source(RoomCustomState.VOTE_IN)
            .target(RoomCustomState.CREATED_DB_STATE)
            .event(RoomCustomEvent.SETTLEMENT_DELAY_TASK)
            .and()

            //投票中 -> 已結算 = 投票結算
            .withExternal()
            .source(RoomCustomState.VOTE_IN)
            .target(RoomCustomState.ALREADY_SETTLEMENT)
            .event(RoomCustomEvent.VOTE_SETTLEMENT)
            .and()
            
            //投票中 -> 已結算 = 查詢結算
            .withExternal()
            .source(RoomCustomState.VOTE_IN)
            .target(RoomCustomState.ALREADY_SETTLEMENT)
            .event(RoomCustomEvent.QUERY_SETTLEMENT)
            .and()
            
            //投票中 -> 已結算 = 投票取消比賽(退還所有人蜜汁)
            .withExternal()
            .source(RoomCustomState.VOTE_IN)
            .target(RoomCustomState.ALREADY_SETTLEMENT)
            .event(RoomCustomEvent.VOTE_RETURN_MONEY)
            .and()
            
            //已開局 -> 已結算 = 投票取消比賽(退還所有人蜜汁)
            .withExternal()
            .source(RoomCustomState.STARTED)
            .target(RoomCustomState.ALREADY_SETTLEMENT)
            .event(RoomCustomEvent.VOTE_RETURN_MONEY)
            .and()
            
            //已結算 -> 已創建 = 全部投票
            .withExternal()
            .source(RoomCustomState.ALREADY_SETTLEMENT)
            .target(RoomCustomState.CREATED_DB_STATE)
            .event(RoomCustomEvent.ALL_VOTE)
            .and()
            
            //已結算 -> 已創建 = 結算延時任務(2小時)
            .withExternal()
            .source(RoomCustomState.ALREADY_SETTLEMENT)
            .target(RoomCustomState.CREATED_DB_STATE)
            .event(RoomCustomEvent.SAVE_RECORD_DELAY_TASK)
            .and()
            ;
    }
}

3.Action

package com.qty.arena.room.custom.statemachine.action;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;

import com.qty.arena.dto.RoomCustomActionDeliveryDTO;
import com.qty.arena.helper.custom.room.RoomCustomCreateLolTaskHelper;
import com.qty.arena.helper.custom.room.RoomCustomSignOutHelper;
import com.qty.arena.helper.custom.room.RoomCustomStateMachineHelper;
import com.qty.arena.helper.custom.room.RoomCustomVoteReturnRoomHelper;
import com.qty.arena.room.custom.statemachine.RoomCustomEvent;
import com.qty.arena.room.custom.statemachine.RoomCustomState;

/**
 * 已創建db房間狀態
 * 
 * @author yanLong.Li
 * @date 2018年11月10日 下午8:28:37
 */
@Component
public class RoomCustomCreateEntryAction implements Action<RoomCustomState, RoomCustomEvent> {

    @Autowired
    private RoomCustomCreateLolTaskHelper roomCustomCreateLolTaskHelper;

    @Autowired
    private RoomCustomSignOutHelper roomCustomSignOutHelper;

    @Autowired
    private RoomCustomVoteReturnRoomHelper roomCustomVoteReturnRoomHelper;

    @Override
    public void execute(StateContext<RoomCustomState, RoomCustomEvent> context) {
        Object object = context.getMessageHeader(RoomCustomStateMachineHelper.ROOM_ACTION_DELIVERY_DTO);
        if (!(object instanceof RoomCustomActionDeliveryDTO)) {
            return;
        }
        RoomCustomEvent event = context.getEvent();
        RoomCustomActionDeliveryDTO roomCustomActionDeliveryDTO = (RoomCustomActionDeliveryDTO) object;
        logic(roomCustomActionDeliveryDTO, event);
    }

    private void logic(RoomCustomActionDeliveryDTO roomCustomActionDeliveryDTO , RoomCustomEvent event) {
        if(event == RoomCustomEvent.CREATE_LOL_ROOM_TASK) {            
            roomCustomCreateLolTaskHelper.createLolTask(roomCustomActionDeliveryDTO);            
        }/*else if(event == RoomCustomEvent.SETTLEMENT_DELAY_TASK) {
            RoomCustomDTO roomCustomDTO = roomCustomActionDeliveryDTO.getRoomCustomDTO();
            roomCustomHelper.settlementAndSaveRecord(roomCustomDTO);
        }else if(event == RoomCustomEvent.SAVE_RECORD_DELAY_TASK) {
            RoomCustomDTO roomCustomDTO = roomCustomActionDeliveryDTO.getRoomCustomDTO();
            roomCustomHelper.saveRecord(roomCustomDTO);
        }*/else if(event == RoomCustomEvent.SIGN_OUT) {
            roomCustomSignOutHelper.preparedSignOut(roomCustomActionDeliveryDTO);
        }else if(event == RoomCustomEvent.ALL_VOTE) {
            roomCustomVoteReturnRoomHelper.execute(roomCustomActionDeliveryDTO);
        }
    }
}

4.演示調用

TeamMatchStateMachineHelper.getInstance().sendEvent(teamMatch.getStateMachineId(), TeamMatchEvent.CREATE
            , TeamMatchStateMachineHelper.TEAM_ACTION_DELIVERY, TeamMatchActionDeliveryDTO.valueOf(teamMatch, arena.getId()));

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM