22. Context Integration
通过监听它的事件或者使用状态和转换的动作来进行与状态机的交互是有点有限的。 这种方法的时间过于有限,并且会与状态机正在处理的应用程序产生交互。 对于这个特定的用例,我们做了一个spring风格的上下文集成,可以轻松地将状态机功能附加到你的bean中。
可用的注解已被统一以允许访问与第21章监听状态机事件中可用的相同的状态机执行点。
@WithStateMachine注释可以用来将状态机与现有的bean关联起来。 然后可以开始向该bean的方法添加支持的注释。
@WithStateMachine
public class Bean1 {
@OnTransition
public void anyTransition() {
}
}
也可以通过使用注释名称字段从应用程序上下文附加到任何其他状态机。
@WithStateMachine(name = "myMachineBeanName")
public class Bean2 {
@OnTransition
public void anyTransition() {
}
}
有时使用状态机ID更方便,这是用户可以设置的,以便更好地识别多个实例。 该ID映射到StateMachine接口中的getId()方法。
@WithStateMachine(id = "myMachineId")
public class Bean16 {
@OnTransition
public void anyTransition() {
}
}
@WithStateMachine也可以作为一个元注释使用,如上所示。 在这种情况下,你可以用WithMyBean注释你的bean。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@WithStateMachine(name = "myMachineBeanName")
public @interface WithMyBean {
}
这些方法的返回类型并不重要,并且被有效地丢弃。
22.1 Enabling Integration
@WithStateMachine的所有功能都可以通过使用注释@EnableWithStateMachine来启用,它只需将所需配置导入到Spring应用程序上下文中。 @EnableStateMachine和@EnableStateMachineFactory都已经注解,所以用户不需要再次添加它。 但是,如果机器是在不使用配置适配器的情况下构建和配置的,则必须使用@EnableWithStateMachine才能使用带有@WithStateMachine的功能。 这个想法如下所示。
public static StateMachine<String, String> buildMachine(BeanFactory beanFactory) throws Exception {
Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.machineId("myMachineId")
.beanFactory(beanFactory);
builder.configureStates()
.withStates()
.initial("S1")
.state("S2");
builder.configureTransitions()
.withExternal()
.source("S1")
.target("S2")
.event("E1");
return builder.build();
}
@WithStateMachine(id = "myMachineId")
static class Bean17 {
@OnStateChanged
public void onStateChanged() {
}
}
Important
如果状态机不是作为Bean创建的,则必须为上述机器设置BeanFactory。 否则机器将不知道调用你的@WithStateMachine方法的处理程序。
22.2 Method Parameters
每个注解都支持完全相同的一组可能的方法参数,但运行时行为因注释本身和调用注释方法的阶段而不同。 为了更好地理解上下文如何工作,请参见第19章使用StateContext。
有关方法参数之间的差异,请参阅下面的单个注释文档。
有效地,所有注释的方法都是使用在过程中动态构建的Spring SPel表达式来调用的。 为了使这个工作成为可能,这些表达式需要有一个它所评估的根对象。 这个根对象是一个StateContext,我们也在内部进行了一些调整,以便可以直接访问StateContext方法而无需通过上下文句柄。
最简单的方法参数自然就是一个StateContext本身。
@WithStateMachine
public class Bean3 {
@OnTransition
public void anyTransition(StateContext<String, String> stateContext) {
}
}
其余的StateContext内容可以被访问,如下所示。 参数数量或顺序无关紧要。
@WithStateMachine
public class Bean4 {
@OnTransition
public void anyTransition(
@EventHeaders Map<String, Object> headers,
ExtendedState extendedState,
StateMachine<String, String> stateMachine,
Message<String> message,
Exception e) {
}
}
22.3 Transition Annotations
转换的注解是OnTransition,OnTransitionStart和OnTransitionEnd。
这些注解行为完全相同,我们来看看如何使用OnTransition。 在此注解中,属性的源和目标可用于限定转换。 如果源和目标为空,则任何转换都匹配。
@WithStateMachine
public class Bean5 {
@OnTransition(source = "S1", target = "S2")
public void fromS1ToS2() {
}
@OnTransition
public void anyTransition() {
}
}
默认@OnTransition注解不能与状态和事件枚举用户由于Java语言限制而创建,因此必须使用字符串表示。
另外,可以通过向方法添加所需的参数来访问Event Headers和ExtendedState。 然后使用这些参数自动调用方法。
@WithStateMachine
public class Bean6 {
@StatesOnTransition(source = States.S1, target = States.S2)
public void fromS1ToS2(@EventHeaders Map<String, Object> headers, ExtendedState extendedState) {
}
}
然而,如果你想有一个类型安全的注解,可以创建一个新的注释并使用@OnTransition作为元注释。 这个用户级别注释可以引用实际状态和事件枚举,框架将尝试以相同的方式匹配这些。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@OnTransition
public @interface StatesOnTransition {
States[] source() default {};
States[] target() default {};
}
上面我们创建了一个@StatesOnTransition注释,它将源和目标定义为类型安全的方式。
@WithStateMachine
public class Bean7 {
@StatesOnTransition(source = States.S1, target = States.S2)
public void fromS1ToS2() {
}
}
在你自己的bean中,你可以使用这个@StatesOnTransition并且使用类型安全的源和目标。
22.4 State Annotations
状态的注解是OnStateChanged,OnStateEntry和OnStateExit。
@WithStateMachine
public class Bean8 {
@OnStateChanged
public void anyStateChange() {
}
}
以与转换注解相同的方式,可以定义目标和源状态。
@WithStateMachine
public class Bean9 {
@OnStateChanged(source = "S1", target = "S2")
public void stateChangeFromS1toS2() {
}
}
对于类型安全性,需要为OnStateChanged作为元注释创建枚举的新注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@OnStateChanged
public @interface StatesOnStates {
States[] source() default {};
States[] target() default {};
}
@WithStateMachine
public class Bean10 {
@StatesOnStates(source = States.S1, target = States.S2)
public void fromS1ToS2() {
}
}
状态进入和退出的方法以相同的方式表现。
@WithStateMachine
public class Bean11 {
@OnStateEntry
public void anyStateEntry() {
}
@OnStateExit
public void anyStateExit() {
}
}
22.5 Event Annotation
有一个名为OnEventNotAccepted的事件相关注释。 通过使用注释定义事件属性,可以仅侦听特定事件。
@WithStateMachine
public class Bean12 {
@OnEventNotAccepted
public void anyEventNotAccepted() {
}
@OnEventNotAccepted(event = "E1")
public void e1EventNotAccepted() {
}
}
22.6 State Machine Annotations
状态机的注解是OnStateMachineStart,OnStateMachineStop和OnStateMachineError。
在状态机期间调用启动和停止生命周期方法
@WithStateMachine
public class Bean13 {
@OnStateMachineStart
public void onStateMachineStart() {
}
@OnStateMachineStop
public void onStateMachineStop() {
}
}
如果状态机出现异常错误,则调用下面的注释。
@WithStateMachine
public class Bean14 {
@OnStateMachineError
public void onStateMachineError() {
}
}
22.7 Extended State Annotation
有一个名为OnExtendedStateChanged的扩展状态相关注解。 也可以只为特定的键更改监听更改。
@WithStateMachine
public class Bean15 {
@OnExtendedStateChanged
public void anyStateChange() {
}
@OnExtendedStateChanged(key = "key1")
public void key1Changed() {
}
}