资讯专栏INFORMATION COLUMN

spring statemachine的企业可用级开发指南2-先跑起来

lvzishen / 1704人阅读

摘要:先来一个,它的主要作用就告诉状态机的初始状态应该啥样,然后把整个状态流程都用代码配置出来。继承了类,表明身份,我就是来配置状态机的初始状态,并描绘一下状态流程的全过程。

上一篇说了很多废话,这一篇就不唠叨,先跑起来

1、来个spring boot
去start.spring.io新建一个springboot的项目,虽然我对spirngboot也有不少的牢骚,但作为demo的开始,还是一个很好用的脚手架,记得选spring statemachine,为了方便,我还选了web 模块

点击generate project 下载到本地,用IDE打开,顺便说一句,我用的是java IDE界逼格很低的eclipse,因为我一直用它,还不要钱。

2、跑起来一个废物例子

在本地打开后我们首先看pom.xml文件,里面和我们相关的有这几段

        2.0.1.RELEASE


            org.springframework.statemachine
            spring-statemachine-starter



                org.springframework.statemachine
                spring-statemachine-bom
                ${spring-statemachine.version}
                pom
                import
    现在就可以在springboot里面用statemachine了,然后我们就开始想办法跑起来。
    先来一个StateMachineConfig,它的主要作用就告诉状态机的初始状态应该啥样,然后把整个状态流程都用代码配置出来。@Configuration是springboot的注解,表示这个类负责配置,@EnableStateMachine表示这个配置类是用在spring statemachine上面的。
package com.skyblue.statemachine.config;

import java.util.EnumSet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;


@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void configure(StateMachineStateConfigurer states) throws Exception {
        states.withStates().initial(OrderStates.UNPAID).states(EnumSet.allOf(OrderStates.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
        transitions.withExternal().source(OrderStates.UNPAID).target(OrderStates.WAITING_FOR_RECEIVE).event(OrderEvents.PAY).and()
                .withExternal().source(OrderStates.WAITING_FOR_RECEIVE).target(OrderStates.DONE).event(OrderEvents.RECEIVE);
    }
}

它配套需要OrderStates和OrderEvents,代码如下:

package com.skyblue.statemachine.config;

public enum OrderStates {
    UNPAID, // 待支付
    WAITING_FOR_RECEIVE, // 待收货
    DONE // 结束
}
package com.skyblue.statemachine.config;
public enum OrderEvents {
    PAY, // 支付
    RECEIVE // 收货
}

还有个OrderSingleEventConfig

package com.skyblue.statemachine.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;

@WithStateMachine(name="orderSingleMachine")
public class OrderSingleEventConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
    
    /**
     * 当前状态UNPAID
     */
    @OnTransition(target = "UNPAID")
    public void create() {
        logger.info("---订单创建,待支付---");
    }
    
    /**
     * UNPAID->WAITING_FOR_RECEIVE 执行的动作
     */
    @OnTransition(source = "UNPAID", target = "WAITING_FOR_RECEIVE")
    public void pay() {
        logger.info("---用户完成支付,待收货---");
    }
    
    /**
     * WAITING_FOR_RECEIVE->DONE 执行的动作
     */
    @OnTransition(source = "WAITING_FOR_RECEIVE", target = "DONE")
    public void receive() {
        logger.info("---用户已收货,订单完成---");
    }

}

因为我本人不会用单元测试,我用了一个controller来运行,大家见谅

@RestController
@RequestMapping("/statemachine")
public class StateMachineController {
    
    @Autowired
    private StateMachine orderSingleMachine;
    
    @RequestMapping("/testSingleOrderState")
    public void testSingleOrderState() throws Exception {

        // 创建流程
        orderSingleMachine.start();

        // 触发PAY事件
        orderSingleMachine.sendEvent(OrderEvents.PAY);

        // 触发RECEIVE事件
        orderSingleMachine.sendEvent(OrderEvents.RECEIVE);

        // 获取最终状态
        System.out.println("最终状态:" + orderSingleMachine.getState().getId());
    }
}

访问页面http://localhost:port/statemachine/testSingleOrderState,页面没有变化,我们看console的日志

2019-04-25 19:14:11.782  INFO 17020 --- [nio-9991-exec-3] tConfig$$EnhancerBySpringCGLIB$$ab30f59f : ---订单创建,待支付---
2019-04-25 19:14:11.787  INFO 17020 --- [nio-9991-exec-3] o.s.s.support.LifecycleObjectSupport     : started org.springframework.statemachine.support.DefaultStateMachineExecutor@2648176e
2019-04-25 19:14:11.787  INFO 17020 --- [nio-9991-exec-3] o.s.s.support.LifecycleObjectSupport     : started UNPAID DONE WAITING_FOR_RECEIVE  / UNPAID / uuid=93e4f752-55bc-40ef-84e4-6c00cf5a4fc5 / id=null
2019-04-25 19:14:11.797  INFO 17020 --- [nio-9991-exec-3] tConfig$$EnhancerBySpringCGLIB$$ab30f59f : ---用户完成支付,待收货---
2019-04-25 19:14:11.800  INFO 17020 --- [nio-9991-exec-3] tConfig$$EnhancerBySpringCGLIB$$ab30f59f : ---用户已收货,订单完成---
最终状态:DONE

3、我们来讲一下这个例子

1)描述上图

其实OrderStates表达的就是这张图的状态(state),OrderEvents表达的就是这张图状态间的事件(event),我们的业务代码就是要塞到事件(event)里面去,处理在状态转换间要处理的事情,比如P从UNPAID到WAITING_FOR_RECEIVE中间的PAY事件(event),我们就可能需要调用支付接口,或者判断用户的会员等级是不是有支付优惠啥的。但state和event是指描述这个流程的三个点和两条线,具体的流程指向要怎么描述呢,就轮到StateMachineConfig出场了。StateMachineConfig继承了EnumStateMachineConfigurerAdapter类,表明身份,我就是来配置状态机的初始状态,并描绘一下状态流程的全过程。

2)塞入业务代码

现在我们知道状态(state)和事件(event)了,也描绘了这个状态机的流程和初始状态是什么样了,然后我们要做什么,当然是开始把业务代码塞到事件(event)里面去,于是OrderSingleEventConfig登场了。OrderSingleEventConfig里面的create,pay和receive方法就是描绘事件触发时需要做什么,但这三个方法名其实是可以自己随便写的(当然最好和event名一样,避免一年后自己看代码时骂当年自己为什么那么蠢,至于别人阅读你的代码嘛......业务代码谁要看你的,别人会重写的),真正和上面描绘的状态流程对应的是@OnTransition,source代表现在的状态,target代表目标状态,很容易懂的。

3)运行状态机

我们在需要的地方引入一个状态机

@Autowired
private StateMachine orderSingleMachine;

然后运行就可以啦

// 创建流程

orderSingleMachine.start();
// 触发PAY事件
orderSingleMachine.sendEvent(OrderEvents.PAY);
// 触发RECEIVE事件
orderSingleMachine.sendEvent(OrderEvents.RECEIVE);
// 获取最终状态
System.out.println("最终状态:" + orderSingleMachine.getState().getId());

至此,状态机就跑起来了,谢谢大家,本教程到此结束,有疑问我也没办法。

4、我是还不想结束的番外篇

现实的世界那有这么简单,这样的一个例子在企业级的开发中毫无用处,大家可以想想这个例子有啥用,其实什么问题都没有解决。大家能想到有哪些问题呢,我用我浅薄的开发经验一眼就看到了以下几个问题:

1)整个项目只有一种状态机流程,我要是想在一个项目里面又有订单流程,又有公文审批流程怎么办,难道和老板讲我的状态机demo告诉我,状态机的流程只能选一个?

2)整个项目只有一个状态机流程,你没有看眼花,这是另外一个问题。哪怕是只有一种流程,比如订单流程,其实也是有很多订单的流程在同时跑,而不是像这个例子,全部订单共用一个流程,一个订单到WAITING_FOR_RECEIVE状态了,其他订单就不能是UNPAY状态了。

3)参数问题,我们做项目,不是为了看日志打出“---订单创建,待支付---”给我们玩的,而是要处理具体业务的,拿订单流程来说吧,订单号怎么传,状态改变时间怎么回数据库,等等问题其实都需要解决。

4)存储问题,状态机如果有多个,需要的时候从哪去,暂时不需要了放哪去,这都是问题,所以存储状态机也是需要解决的。

下一章教程,我们带着这四个灵魂问题继续我们的学习之旅

配套代码地址

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74940.html

相关文章

  • spring statemachine企业可用开发指南1-说些废话

    摘要:让我们先看下状态机的概念。下面是状态机模型中的个要素,即现态条件动作次态。因为订单和审批公文都有很多的流程,每个流程都会产生状态的变化,而且流程是这种业务的主轴,其他都是围绕这个流程和状态变化来考虑的,所以看起来蛮适合用状态机来做。 1、背景在我打算学习spring statemachine的时候,我几乎看过了所有网上的中文教程,基本上都处于浅尝辄止的阶段,有几篇讲的比较深入的,都只是...

    BakerJ 评论0 收藏0
  • spring statemachine企业可用开发指南6-持久化

    摘要:目前为止,我们都是从状态流程的开始阶段创建一个状态机,然后一路走下去。然后就可以愉快的在里面看怎么用了发送事件持久化恢复状态机后的状态为执行完保存后,大家可以自己在客户端查看以下,是不是有内容保存进去了。 目前为止,我们都是从状态流程的开始阶段创建一个状态机,然后一路走下去。但在实际业务中,状态机可能需要在某个环节停留,等待其他业务的触发,然后再继续下面的流程。比如订单,可能在支付环节...

    Jioby 评论0 收藏0
  • spring statemachine企业可用开发指南7-伪持久化和中间段状态机

    摘要:在实际的企业开发中,不可能所有情况都是从头到尾的按状态流程来,会有很多意外,比如历史数据,故障重启后的遗留流程,所以这种可以任意调节状态的才是我们需要的状态机。 1、伪持久化和中间段的状态机我们设想一个业务场景,就比如订单吧,我们一般的设计都会把订单状态存到订单表里面,其他的业务信息也都有表保存,而状态机的主要作用其实是规范整个订单业务流程的状态和事件,所以状态机要不要保存真的不重要,...

    shiyang6017 评论0 收藏0
  • spring statemachine企业可用开发指南5-传递参数message

    摘要:创建了后,状态机就可以不只是传一个,可以组合和数据内容一起发送给状态机变化的处理类了。到这里为止,状态机通过对象就和其他的业务代码做到了数据连接。 在企业开发中,数据在不同的业务间传输是最常见的工作,所以虽然我们的主架构是用的状态机,也就是从流程状态的角度来看待这个项目,但在具体业务中,每个状态的转变中会牵涉到各类业务,这些业务有些需要收到状态机变化的通知,需要把状态值传递给业务类和业...

    YacaToy 评论0 收藏0
  • spring statemachine企业可用开发指南4-多种状态机共存

    摘要:目前为止,多个状态机和多种状态机都可以在里面实现了,下一章我们来解决下状态机和实际业务间的数据传输问题,毕竟我们不是为了让状态机自个独自玩耍,和业务数据互通有无才是企业开发的正道。 在上一章的例子中,我们实现了多个状态机并存执行,不同的订单有各自的状态机运行,但只有一种状态机,这显然不能满足实际业务的要求,比如我就遇到了订单流程和公文审批流程在同一个项目的情况,所以我们这一章讲怎么让多...

    boredream 评论0 收藏0

发表评论

0条评论

lvzishen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<