21. 监听状态机事件
有一些用例只是想知道状态机发生了什么,对某些事情做出反应或仅仅为了调试目的而进行日志记录。 SSM提供了用于添加监听器的接口,然后在出现各种状态更改,动作等时提供选项以获得回调。
你基本上有两个选择,要么监听Spring应用程序上下文事件,要么直接将监听器附加到状态机。 这些基本上都会提供相同的信息,其中一个通过监听器接口将事件生成为事件类和其他生成回调。 这两者都有优点和缺点,将在稍后讨论。
21.1 应用程序上下文事件
应用程序上下文事件类包括OnTransitionStartEvent,OnTransitionEvent,OnTransitionEndEvent,OnStateExitEvent,OnStateEntryEvent,OnStateChangedEvent,OnStateMachineStart和OnStateMachineStop等扩展基本事件类StateMachineEvent的类。这些类可以像Spring类型的ApplicationListener一样使用。
StateMachine将通过设置的StateMachineEventPublisher发送上下文事件。 如果@Configuration类使用@EnableStateMachine注释,则会自动创建默认实现。
public class StateMachineApplicationEventListener
implements ApplicationListener<StateMachineEvent> {
@Override
public void onApplicationEvent(StateMachineEvent event) {
}
}
@Configuration
public class ListenerConfig {
@Bean
public StateMachineApplicationEventListener contextListener() {
return new StateMachineApplicationEventListener();
}
}
上下文事件也通过@EnableStateMachine自动启用,状态机机构建器(builder)StateMachine注册为bean,如下所示。
@Configuration
@EnableStateMachine
public class ManualBuilderConfig {
@Bean
public StateMachine<String, String> stateMachine() throws Exception {
Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureStates()
.withStates()
.initial("S1")
.state("S2");
builder.configureTransitions()
.withExternal()
.source("S1")
.target("S2")
.event("E1");
return builder.build();
}
}
21.2 状态机监听器
使用StateMachineListener,可以扩展它并实现所有回调方法,也可以使用包含存根方法实现的StateMachineListenerAdapter类,并选择要覆盖哪些方法。
public class StateMachineEventListener
extends StateMachineListenerAdapter<States, Events> {
@Override
public void stateChanged(State<States, Events> from, State<States, Events> to) {
}
@Override
public void stateEntered(State<States, Events> state) {
}
@Override
public void stateExited(State<States, Events> state) {
}
@Override
public void transition(Transition<States, Events> transition) {
}
@Override
public void transitionStarted(Transition<States, Events> transition) {
}
@Override
public void transitionEnded(Transition<States, Events> transition) {
}
@Override
public void stateMachineStarted(StateMachine<States, Events> stateMachine) {
}
@Override
public void stateMachineStopped(StateMachine<States, Events> stateMachine) {
}
@Override
public void eventNotAccepted(Message<Events> event) {
}
@Override
public void extendedStateChanged(Object key, Object value) {
}
@Override
public void stateMachineError(StateMachine<States, Events> stateMachine, Exception exception) {
}
@Override
public void stateContext(StateContext<States, Events> stateContext) {
}
}
在上面的例子中,我们简单地创建了我们自己的监听器类StateMachineEventListener,它扩展了StateMachineListenerAdapter。
Listener方法stateContext提供对不同阶段的各种StateContext更改的访问。 更多关于它的章节,第19章,使用StateContext。
一旦你定义了你自己的监听器,它可以通过它的接口注册到状态机中,如下所示。 这只是一个味道问题,如果它被连接在弹簧配置中或在任何应用程序生命周期中手动完成。
public class Config7 {
@Autowired
StateMachine<States, Events> stateMachine;
@Bean
public StateMachineEventListener stateMachineEventListener() {
StateMachineEventListener listener = new StateMachineEventListener();
stateMachine.addStateListener(listener);
return listener;
}
}
21.3 限制和问题
Spring应用程序上下文并不是最快的事件总线,因此建议您考虑一下事件状态机正在发送的事件的速率。 为了获得更好的性能,最好使用StateMachineListener接口。 由于这个特定的原因,可以在@EnableStateMachine和@EnableStateMachineFactory中使用contextEvents标志来禁用Spring应用程序上下文事件,如上所示。
@Configuration
@EnableStateMachine(contextEvents = false)
public class Config8
extends EnumStateMachineConfigurerAdapter<States, Events> {
}
@Configuration
@EnableStateMachineFactory(contextEvents = false)
public class Config9
extends EnumStateMachineConfigurerAdapter<States, Events> {
}