Java 如何从StateMachineConfiguration创建StateMachine

Java 如何从StateMachineConfiguration创建StateMachine,java,spring,spring-statemachine,Java,Spring,Spring Statemachine,我有一个基于注释的状态机配置: @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) @EnableStateMachine(name = "machine1") public class Machine1 extends EnumStateMachineConfigurerAdapter<SimStates, SimEvents> { @Override public void configure(StateMach

我有一个基于注释的状态机配置:

@Component @Scope(BeanDefinition.SCOPE_PROTOTYPE)
@EnableStateMachine(name = "machine1")
public class Machine1 extends
   EnumStateMachineConfigurerAdapter<SimStates, SimEvents> {

   @Override
   public void configure(StateMachineStateConfigurer<SimStates, SimEvents> states) throws Exception {
      states.withStates()
        .initial(INIT)
        .state(INIT)
        .state(S1)
        .state(FINISH)
        .end(FINISH)
      ;
    }
  ...
更新:我将“无完整应用程序上下文”更新为“无隐式调用
getBean(“machine1”)
”。这个问题还涉及了解spring状态机的所有工厂、适配器、配置和配置程序

我宁愿实例化Machine1并将其提供给某个构建器, 用于获取状态机实例的配置程序或适配器

支持基于注释的实例化配置(如via)或a-没有其他选项

使用绝对不会创建完整的应用程序上下文:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = { Machine1.class})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class StateMachineTests {

    @Autowired
    private StateMachine<String, String> machine1;

    @Test
    public void testInitialState() throws Exception {
        StatMachineTestPlan<SimState, SimEvent> plan = StateMachineTestPlanBuilder.<SimState, SimEvent>builder()
          .defaultAwaitTime(2)
          .stateMachine(machine1)
          .step()
            .expectStateChange(1)
            .expectStateEntered(SimState.INITIAL)
            .expectState(SimState.INITIAL)
          .and()
          .build()

      plan.test();
    }

}
有关测试的详细官方文件可用

我不希望有一个隐含的 通过 StateMachineFactory.getStateMachine(“machine1”),这将需要 应用程序上下文

通过构建器创建SM不需要任何Spring上下文

public class TestEventNotAccepted {

    @Test
    public void testEventNotAccepted() throws Exception {
        StateMachine<String, String> machine = buildMachine();
        StateMachineTestPlan<String, String> plan =
                StateMachineTestPlanBuilder.<String, String>builder()
                        .defaultAwaitTime(2)
                        .stateMachine(machine)
                        .step()
                        .expectStates("SI")
                        .and()
                        .step()
                        .sendEvent("E2")
                        .and()
                        .build();
        plan.test();
    }

    private StateMachine<String, String> buildMachine() throws Exception {
        StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();

        builder.configureConfiguration()
                .withConfiguration()
                .taskExecutor(new SyncTaskExecutor())
                .listener(customListener())
                .autoStartup(true);

        builder.configureStates()
                .withStates()
                .initial("SI")
                .state("S1")
                .state("S2");

        builder.configureTransitions()
                .withExternal()
                .source("SI").target("S1")
                .event("E1")
                .action(c -> c.getExtendedState().getVariables().put("key1", "value1"))
                .and()
                .withExternal()
                .source("S1").target("S2").event("E2");

        return builder.build();
    }

    private StateMachineListener<String, String> customListener() {
        return new StateMachineListenerAdapter<String, String>() {
            @Override
            public void eventNotAccepted(Message event) {
                System.out.println("EVENT NOT ACCEPTED: " + event);
            }
        };
    }
公共类TestEventNotAccepted{
@试验
public void testEventNotAccepted()引发异常{
StateMachine=buildMachine();
状态机测试计划=
StateMachineTestPlanBuilder.builder()
.2.预计时间(2)
.状态机
.第()步
.预期状态(“SI”)
.及()
.第()步
.sendEvent(“E2”)
.及()
.build();
计划。测试();
}
私有StateMachine buildMachine()引发异常{
StateMachineBuilder.Builder=StateMachineBuilder.Builder();
builder.configureConfiguration()
.withConfiguration()
.taskExecutor(新的SyncTaskExecutor())
.listener(customListener())
.自动启动(正确);
builder.configureStates()
.with states()
.首字母(“SI”)
.州(“S1”)
.州(“S2”);
builder.configureTransitions()
.withExternal()
.来源(“SI”).目标(“S1”)
.事件(“E1”)
.action(c->c.getExtendedState().getVariables().put(“key1”、“value1”))
.及()
.withExternal()
.来源(“S1”).目标(“S2”).事件(“E2”);
返回builder.build();
}
私有StateMachineListener customListener(){
返回新的StateMachineListenerAdapter(){
@凌驾
未接受公共无效事件(消息事件){
System.out.println(“未接受事件:+事件”);
}
};
}

我没有找到一种明确的方法将
EnumStateMachineConfigureAdapter
StateMachineBuilder.Builder
一起使用,但我使用了这种方法:

@组件
@范围(BeanDefinition.Scope\u原型)
@EnableStateMachine(name=“machine1”)
公共类Machine1扩展了EnumStateMachineConfigureAdapter{
@凌驾
public void configure(StateMachineStateConfigurer状态)引发异常{
配置状态(状态);
}
@凌驾
public void configure(StateMachineTransitionConfigurer转换)引发异常{
配置转换(转换);
}
公共静态无效配置状态(StateMachineStateConfigurer状态)引发异常{
states.with states()
.首字母(INIT)
.state(INIT)
.州(S1)
.状态(完成)
.结束(完成);
}
公共静态无效配置转换(StateMachineTransitionConfigurer状态)引发异常{
states.withTransitions()
//配置转换
;
}
}
以及在Statemachine测试中导入静态配置方法:

导入静态com.example.statemachine.Machine1.configureStates;
导入静态com.example.statemachine.Machine1.configureTransitions;
不接受公共类测试{
@试验
public void testEventNotAccepted()引发异常{
StateMachine=buildMachine();
状态机测试计划=
StateMachineTestPlanBuilder.builder()
.2.预计时间(2)
.状态机
.第()步
.ExpectState(初始)
.及()
//配置其他测试步骤
.build();
计划。测试();
}
私有StateMachine buildMachine()引发异常{
StateMachineBuilder.Builder=StateMachineBuilder.Builder();
builder.configureConfiguration()
.withConfiguration()
.taskExecutor(新的SyncTaskExecutor())
.listener(customListener())
.自动启动(正确);
配置状态(builder.configureStates());
configureTransitions(builder.configureTransitions());
返回builder.build();
}
}

因此,我能够在不构建整个Spring上下文和不使用
@SpringBootTest

的情况下对我的精确配置进行单元测试。谢谢,我知道
StateMachineTestPlan
。我还知道如何使用
StateMachineBuilder
创建动态状态机。但我的问题是如何从
EnumSta获取动态状态机TeMachineConfigureAdapter
实例到没有应用程序上下文(因此没有默认的
StateMachineFactory
)的
StateMachine实例。我明白了-但是为什么需要完整的应用程序上下文来测试EnumSM实例?可以使用SpringBootTest(classes=EnumSM.class)并嘲笑其他一切(如SM守卫/SM行动/服务等)对,很好。我有fe
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-test</artifactId>
    <version>2.0.3.RELEASE</version>  // change version to match yours
    <scope>test</scope>
</dependency>
public class TestEventNotAccepted {

    @Test
    public void testEventNotAccepted() throws Exception {
        StateMachine<String, String> machine = buildMachine();
        StateMachineTestPlan<String, String> plan =
                StateMachineTestPlanBuilder.<String, String>builder()
                        .defaultAwaitTime(2)
                        .stateMachine(machine)
                        .step()
                        .expectStates("SI")
                        .and()
                        .step()
                        .sendEvent("E2")
                        .and()
                        .build();
        plan.test();
    }

    private StateMachine<String, String> buildMachine() throws Exception {
        StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();

        builder.configureConfiguration()
                .withConfiguration()
                .taskExecutor(new SyncTaskExecutor())
                .listener(customListener())
                .autoStartup(true);

        builder.configureStates()
                .withStates()
                .initial("SI")
                .state("S1")
                .state("S2");

        builder.configureTransitions()
                .withExternal()
                .source("SI").target("S1")
                .event("E1")
                .action(c -> c.getExtendedState().getVariables().put("key1", "value1"))
                .and()
                .withExternal()
                .source("S1").target("S2").event("E2");

        return builder.build();
    }

    private StateMachineListener<String, String> customListener() {
        return new StateMachineListenerAdapter<String, String>() {
            @Override
            public void eventNotAccepted(Message event) {
                System.out.println("EVENT NOT ACCEPTED: " + event);
            }
        };
    }