Spring statemachine Perist配方有两种状态机配置

时间:2015-11-10 09:04:35

标签: spring state-machine spring-statemachine

我正在尝试将Spring statemachine的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
  • 复制了该配置并将其改编为我的用例
  • 显然给我打了一个类似于我的案例的订单

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

出现的问题是:

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

那么,任何人都可以告诉我在哪里搞砸了吗?或者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";
}

2 个答案:

答案 0 :(得分:0)

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

  1. 如果在使用@EnableStateMachine创建两台计算机时获得NPE,那就是我需要研究的错误。您应该使用带有name的{​​{1}}字段,指示如果要创建多台计算机,则将使用bean名称适配器/ javaconfig。 @EnableStateMachine默认为bean名称@EnableStateMachine,并且具有相同名称的多个stateMachine适配器将尝试配置相同的计算机。对于多台机器,它就像@EnableStateMachine

  2. @EnableStateMachine(name = "sm1")的问题有点明显,但是没有一台机器不能使用您发布的代码,因为我不会在任何地方看到它。通常TaskExecutor要么显式设置实例,要么来自bean工厂(如果已设置)作为后备。可以在配置界面http://docs.spring.io/spring-statemachine/docs/1.0.0.RELEASE/reference/htmlsingle/#statemachine-config-commonsettings中设置这些钩子。

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

  4. 我不会尝试围绕示例中的特定shell概念构建任何应用程序。 Shell只是用来提供从命令行与机器交互的简便方法。我看起来可能通过为所有机器使用不同的bean名称来避免所有麻烦,即StateMachineListenerAdapter

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

答案 1 :(得分:0)

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

然后当使用persist配方时,我需要为statemachine工厂传递statemachine id的字符串参数以获取statemachine实例并传递它以构造处理程序,并使用处理程序进行更新,如示例所示。

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

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

相关问题