#java #spring #spring-statemachine
#java #spring #spring-statemachine
Вопрос:
Я пытаюсь настроить конечный автомат с помощью fork
и join
. После присоединения я хотел бы вызвать действие для состояния присоединения, используя внутренний переход. Проблема в том, что действие, настроенное для withInternal()
, не запускается. Я пробовал .guard(context -> true)
взломать, а также играл с .timer()
и .timerOnce()
, но это тоже не сработало.
Здесь указывается конфигурация:
private void configureStates(StateMachineBuilder.Builder<String, String> builder) throws Exception {
builder.configureStates()
.withStates()
.initial("A")
.fork("B")
.join("C")
.state("A")
.state("B_")
.state("C")
.state("D")
.state("E")
.and()
.withStates()
.parent("B_")
.initial("B1")
.end("C1")
.and()
.withStates()
.parent("B_")
.initial("B2")
.end("C2")
.and()
.withStates()
.parent("B_")
.initial("B3")
.end("C3")
.end("E");
}
Конфигурация переходов:
private void configureTransitions(StateMachineBuilder.Builder<String, String> builder) throws Exception {
builder.configureTransitions()
.withExternal()
.source("A")
.target("B")
.event("E0")
.action(context -> log.info("From A to B"))
.and()
.withInternal()
.source("B")
.guard(stateContext -> true)
.action(context -> log.info("At B"))
.timerOnce(50)
.and()
.withFork()
.source("B")
.target("B_")
.and()
.withExternal()
.source("B1")
.target("C1")
.event("E1")
.and()
.withExternal()
.source("B2")
.target("C2")
.event("E2")
.and()
.withExternal()
.source("B3")
.target("C3")
.and()
.withExternal()
.source("C3")
.target("A")
.event("E3")
.and()
.withJoin()
.source("B_")
.target("C")
.and()
.withInternal()
.source("C")
.guard(context -> true)
.action(context -> log.info("At C"))
.timerOnce(50)
.state("C")
.and()
.withExternal()
.source("C")
.target("D")
.action(context -> log.info("At D"))
.and()
.withInternal()
.source("D")
.guard(stateContext -> true)
.action(stateContext -> log.info("At internal D"))
.timer(10)
.and()
.withExternal()
.source("D")
.event("E4")
.target("E");
}
Я также добавил прослушиватель к конечному автомату:
private StateMachineListener<String, String> listener() {
return new StateMachineListenerAdapter<String, String>() {
@Override
public void stateChanged(State<String, String> from, State<String, String> to) {
log.info("State transited from [{}] to [{}]",
from == null ? null : from.getId(),
to == null ? null : to.getId());
}
};
}
И окончательная конфигурация:
private StateMachine<String, String> buildMachine() throws Exception {
StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.listener(listener())
.autoStartup(true);
configureStates(builder);
configureTransitions(builder);
return builder.build();
}
Проблема в том, что ни одно из внутренних действий перехода не вызывается.
Я создал небольшой тест для данной конфигурации:
@Test
public void testForkJoin() throws Exception {
StateMachine<String, String> machine = buildMachine();
StateMachineTestPlan<String, String> plan = StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(3)
.stateMachine(machine)
.step()
.expectStates("A")
.and()
.step()
.sendEvent("E0")
.expectStates("B_", "B1", "B2", "C3")
.and()
.step()
.sendEvent("E1")
.expectStates("B_", "C1", "B2", "C3")
.and()
.step()
.sendEvent("E3")
.expectState("A")
.and()
.step()
.sendEvent("E0")
.expectStates("B_", "B1", "B2", "C3")
.and()
.step()
.sendEvent("E1")
.expectStates("B_", "C1", "B2", "C3")
.and()
.step()
.sendEvent("E2")
.expectStates("D")
.and()
.step()
.sendEvent("E4")
.expectState("E")
.and()
.build();
plan.test();
}
В качестве обходного пути я добавил несколько внешних переходов (из C
в D
), но правда в том, что я хотел бы опустить state D
и перейти непосредственно к E
, выполнив существующие действия как внутреннее действие перехода.
Ответ №1:
Я хотел бы опустить состояние D и перейти непосредственно к E, выполнив существующие действия в качестве действия внутреннего перехода.
Краткий ответ: вы не можете.
Псевдосостояния Fork / Join не должны вводить спецификацию поведения (например, Action). Fork / Join используются только для моделирования параллельных операций (fork) и синхронизации (join) в SM (переходных псевдосостояниях).
Реализация Spring State Machine соответствует спецификации UML, и из-за этого действия, связанные с fork / join, не выполняются.
Действия связаны либо с конкретным переходом, либо с состоянием.
Действия, связанные с переходами:
При выполнении объединения у вас может быть N (>= 2) источников (J1E, J2E — заключительные этапы этого другого региона), поэтому можно определить разные действия при переходе от J1E к этапу ОБЪЕДИНЕНИЯ (действие = A1) и от J2E к этапу ОБЪЕДИНЕНИЯ (действие = A2).
Действия, связанные с состоянием:
Если у вас есть общее действие, которое необходимо выполнить после синхронизации параллельных операций, вы можете определить его как часть следующего перехода (например, я полагаю, что в вашем случае SM это происходит при переходе с C на D).