如何在Spring State Machine中从Java代码动态设置UML时间事件的周期值
要解决在Spring UML状态机中动态修改时间事件周期的问题,核心是利用Opaque Expression + Spring Bean的组合,让时间事件的周期值可以从Java代码中动态更新。以下是具体步骤和代码示例:
1. 调整UML模型中的Opaque Expression
你已经在TimeEvent2的when部分使用了Opaque Expression并设置了language="bean",只需要微调body内容,使其指向Java Bean的方法(确保能动态获取最新值):
原UML中TimeEvent2的OpaqueExpression body是
mT,建议修改为mT.getDelay(),这样Spring会主动调用Bean的方法获取最新时间值。
对应的UML关键片段:
<packagedElement xmi:type="uml:TimeEvent" xmi:id="_YXALUCE8EeyjDr-lLLuZXg" name="TimeEvent2" visibility="protected" isRelative="true"> <when xmi:type="uml:TimeExpression" xmi:id="_ZjjN0CHHEeyjDr-lLLuZXg"> <expr xmi:type="uml:OpaqueExpression" xmi:id="_ZjjN0SHHEeyjDr-lLLuZXg" name="mT"> <language>bean</language> <body>mT.getDelay()</body> </expr> </when> </packagedElement>
2. 定义Java侧的动态时间提供者Bean
创建一个可动态修改的Bean,用来存储和返回时间事件的周期值,需要提供获取和设置时间值的方法:
import org.springframework.stereotype.Component; @Component("mT") public class DynamicTimeProvider { // 初始周期值(单位:毫秒) private long delay = 2000; // 获取当前周期值 public long getDelay() { return delay; } // 动态更新周期值 public void setDelay(long delay) { this.delay = delay; } }
这里用@Component("mT")指定Bean名称为mT,和UML中OpaqueExpression的body内容完全对应。
3. 在业务代码中动态修改时间值
通过注入DynamicTimeProvider Bean,你可以在任意业务逻辑中修改时间事件的周期值:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class StateMachineService { @Autowired private DynamicTimeProvider mT; // 示例方法:将TimeEvent2的周期更新为3000毫秒 public void updateTimeEventDelay(long newDelay) { mT.setDelay(newDelay); // 后续TimeEvent2触发时会自动使用新的周期值 } }
原理说明
Spring State Machine在解析language="bean"的Opaque Expression时,会使用Spring表达式语言(SpEL)解析body内容:
- 它会查找名称为
mT的Bean,然后调用getDelay()方法获取最新的时间值 - 每次触发时间事件前,都会重新解析这个表达式,所以修改
DynamicTimeProvider中的值后,下一次时间事件会自动应用新周期
替代方案:用原子类简化实现
如果你不想自定义类,也可以用AtomicLong结合Supplier来实现:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; @Configuration public class TimeConfig { @Bean("mT") public Supplier<Long> timeSupplier() { AtomicLong dynamicDelay = new AtomicLong(2000); return dynamicDelay::get; } // 提供修改时间的方法 public void updateTimeDelay(long newDelay) { ((AtomicLong)((SuppliedSupplier) timeSupplier()).getSupplier()).set(newDelay); } }
此时UML中的OpaqueExpression body可以直接写mT,因为Spring会自动调用Supplier的get()方法获取值。
内容的提问来源于stack exchange,提问作者Dev1819

