【问题标题】:Spring statemachine Persist recipe with two state machine configurations具有两种状态机配置的 Spring statemachine Persist recipe
【发布时间】:2016-02-11 02:38:23
【问题描述】:

我正在尝试将弹簧状态机的 Persist 示例扩展到两种不同的状态机配置。 http://docs.spring.io/spring-statemachine/docs/1.0.0.RELEASE/reference/htmlsingle/#statemachine-examples-persist

为此我

  • 添加了新架构
  • 添加了一些测试数据
  • 复制了 Persist、PersistCommand 的代码并根据我的情况进行了调整

到目前为止没什么大不了的。现在进行配置:

  • 我删除了 StateMachineConfig(以及 @EnableStateMachine 注释)
  • 将 StateMachineConfiguration 作为 Bean 添加到 PersistHandlerConfig 中并使用 Builder
  • 复制了该配置并使其适应我的用例
  • 显然为我创建了一个类似 Order for my case 的类

此外,我调整了 AbstractStateMachineCommands 类并在其中自动装配状态机列表。 start/stop 和 state 方法现在启动/停止和打印每个状态机的状态(这里我不关心打印、变量)。

出现的问题是:

  • 持久化不再起作用
  • 我可以启动应用程序和两个状态机
  • 我可以使用所有 persist 和 myUseCase 调用,但无法处理持久数据。
  • 如果我例如调用persist流程1,应用程序将底层SM的状态更改为PROCESSING,但持久化的数据没有改变。
  • 在调试中,我能够解决 LifecycleObjectSupport 中 getTaskExecutor 方法返回 null 的问题(而在原始示例中,bean 工厂返回 SyncTaskExecutor 的实例)。
  • 此外,TestEventListener 似乎不再适用于任何状态机
  • 当我在任何包含状态机 bean 的配置中使用 @EnableStateMachine 时,会在 StateMachineConfiguration 的 afterPropertiesSet 处发生 NPE。

那么,谁能告诉我我在哪里搞砸了?还是 Persist 配方不适用于两个状态机?

非常感谢。

代码示例: Application.java 现在包含这些配置和实体:

    @Configuration
static class PersistHandlerConfig {

    @Bean
    public Persist persist() throws Exception {
        return new Persist(persistStateMachineHandler());
    }

    @Bean
    public PersistStateMachineHandler persistStateMachineHandler() throws Exception {
        return new PersistStateMachineHandler(persistSm());
    }

    @Bean
    public StateMachine<String, String> persistSm() throws Exception{

        Builder<String, String> builder = StateMachineBuilder.builder();
        builder.configureStates()
            .withStates()
                .initial("PLACED")
                .state("PROCESSING")
                .state("SENT")
                .state("DELIVERED");

        builder.configureTransitions()
            .withExternal()
                .source("PLACED").target("PROCESSING")
                .event("PROCESS")
                .and()
            .withExternal()
                .source("PROCESSING").target("SENT")
                .event("SEND")
                .and()
            .withExternal()
                .source("SENT").target("DELIVERED")
                .event("DELIVER");

        return builder.build();
    }
}

@Configuration
static class TicketPersistHandlerConfig {

  @Bean
  public TicketPersist ticketPersist() throws Exception {
    return new TicketPersist(ticketPersistStateMachineHandler());
  }

  @Bean
  public PersistStateMachineHandler ticketPersistStateMachineHandler() throws Exception {
    return new PersistStateMachineHandler(buildMachine());
  }

  @Bean
  public StateMachine<String, String> buildMachine() throws Exception {

       Builder<String, String> builder = StateMachineBuilder.builder();
       builder.configureStates()
           .withStates()
               .initial("PRINTED")
               .state("BOOKED")
               .state("SOLD")
               .state("DELIVERED");

       builder.configureTransitions()
           .withExternal()
               .source("PRINTED").target("BOOKED")
               .event("BOOK")
               .and()
           .withExternal()
               .source("BOOKED").target("SOLD")
               .event("SELL")
               .and()
           .withExternal()
               .source("SOLD").target("DELIVERED")
               .event("DELIVER");

       return builder.build();
   }

}

public static class Order {
    int id;
    String state;

    public Order(int id, String state) {
        this.id = id;
        this.state = state;
    }

    @Override
    public String toString() {
        return "Order [id=" + id + ", state=" + state + "]";
    }

}

public static class Ticket {
  int id;
  String state;

  public Ticket(int id, String state) {
    this.id = id;
    this.state = state;
  }

  @Override
  public String toString() {
    return "Ticket [id=" + id + ", state=" + state + "]";
  }

}

TicketPersist.java 和 TicketPersistCommands.java 与订单的相同(只是将订单替换为票证)。 我通过以下方式调整了 AbstractStateMachineCommands:

@Autowired
private List<StateMachine<S, E>> stateMachines;
@CliCommand(value = "sm start", help = "Start a state machine")
public String start() {
  for (StateMachine<S, E> stateMachine : stateMachines)
  {
    stateMachine.start();
  }
    return "State machines started";
}

@CliCommand(value = "sm stop", help = "Stop a state machine")
public String stop() {
  for (StateMachine<S, E> stateMachine : stateMachines)
  {
    stateMachine.stop();
  }
    return "State machines stopped";
}

【问题讨论】:

  • 对于您可能面临的一些问题,有一些修复(即将发布 1.0.1)。没有看到任何代码就很难发表评论,所以如果可能的话,你介意分享一些你的东西吗?
  • 嗨,我添加了一些示例代码。希望对您有所帮助。
  • 我刚刚修复了与您的问题相关的问题gh119gh120。希望我们下周推出 1.0.1。

标签: spring state-machine spring-statemachine


【解决方案1】:

我尝试使用存储库模型工厂使用 2 组配置来配置状态机工厂,每组都给出一个名称。

然后在使用persist recipe时,我需要传递一个状态机id的字符串参数给状态机工厂来获取状态机实例并将其传递给构造处理程序,并使用处理程序进行更新,如示例所示。

所以问题在于如何使用参数配置处理程序 bean。而不是 @Autowiredthe 处理程序或任何需要处理程序的东西,使用 beanFactory.getBean() 获取它。

仅在配方实施中它就对我有用。但从技术上讲,它应该适用于使用模型工厂的配置。

【讨论】:

    【解决方案2】:

    普通注解配置(使用@EnableStateMachine 和适配器)和手动构建器之间存在概念差异。后者实际上是在 spring 应用程序上下文之外使用的,虽然您可以将使用它创建的机器注册为 bean(就像您尝试做的那样),但很多自动配置并未应用。我可能需要在测试中更加注意这个用例(用户从注册为@Bean 的构建器返回机器)。

    1. 如果在使用@EnableStateMachine 创建两台机器时得到 NPE,这是我需要研究的错误。您应该使用 name 字段和 @EnableStateMachine 指示如果要创建多台机器将使用的 bean 名称适配器/javaconfig。 @EnableStateMachine 默认为 bean 名称 stateMachine 并且具有多个具有相同名称的 @EnableStateMachine 适配器将尝试配置同一台机器。对于多台机器,它会类似于@EnableStateMachine(name = "sm1")

    2. TaskExecutor 的问题很明显,但任何机器都不应该使用您发布的代码,因为我没有看到它在任何地方创建。通常TaskExecutor 要么来自明确设置的实例,要么来自 bean 工厂(如果已设置)作为后备。在配置接口http://docs.spring.io/spring-statemachine/docs/1.0.0.RELEASE/reference/htmlsingle/#statemachine-config-commonsettings 中有设置这些的钩子。

    3. 这些示例默认使用 @EnableStateMachine,它自动进行上下文集成,这意味着 Spring 应用程序上下文事件发布者也已注册(手动构建器的机器不会发生这种情况),因此创建 ApplicationListener 的正常方法为在https://github.com/spring-projects/spring-statemachine/blob/master/spring-statemachine-samples/src/main/java/demo/CommonConfiguration.java#L57 中完成的不再有效。您也可以使用StateMachineListenerAdapter 并通过配置将它们注册到机器上。

    我不会尝试围绕示例中的特定 shell 概念构建任何应用程序。 Shell 只是用来提供从命令行与机器交互的简单方法。我看起来你可以通过为所有机器使用不同的 bean 名称来避免所有麻烦,即@EnableStateMachine(name = "sm1")

    我将尝试根据这些用例创建一些 gh 问题。人们似乎总是以不同的方式尝试使用我们在测试中没有预料到的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 2016-12-07
      • 2016-04-16
      • 2019-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多