javascript
Spring State Machine:它是什么,您需要它吗?
狀態(tài)機(jī)是基于有限狀態(tài)的計算模型 ,正如Wikipedia非常明確地說的那樣。 通常,工作流會與狀態(tài)一起使用,這意味著您不能僅從任何狀態(tài)進(jìn)入任何其他狀態(tài):應(yīng)遵循一些規(guī)則。 這些狀態(tài)之間的轉(zhuǎn)換受規(guī)則限制。
Spring框架具有一個稱為Spring State Machine的完整庫 。 它是該概念的實現(xiàn),旨在為已經(jīng)使用Spring框架的開發(fā)人員簡化狀態(tài)機(jī)邏輯的開發(fā)。
讓我們看看它是如何工作的。
首先,我們需要一個Spring Boot應(yīng)用程序,該應(yīng)用程序依賴于Spring State Machine(為簡化起見,還有Lombok)。 從Spring Starter頁面或從Intellij IDEA之類的IDE(也使用Spring starter模板)生成一個非常容易。
要實際使用狀態(tài)機(jī),應(yīng)在應(yīng)用程序類中啟用它:
@SpringBootApplication @EnableStateMachine public class Application implements CommandLineRunner {private final StateMachine<BookStates, BookEvents> stateMachine;@Autowiredpublic Application(StateMachine<BookStates, BookEvents> stateMachine) {this.stateMachine = stateMachine;}public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Overridepublic void run(String... args) {stateMachine.start();stateMachine.sendEvent(BookEvents.RETURN);stateMachine.sendEvent(BookEvents.BORROW);stateMachine.stop();} }使用@EnableStateMachine批注時,它將在應(yīng)用程序啟動時自動創(chuàng)建默認(rèn)狀態(tài)機(jī)。 因此可以將其注入Application類。 默認(rèn)情況下,該bean將被稱為stateMachine ,但是可以給它另一個名稱。 我們還需要為我們的活動和州提供課程。 讓我們的簡單示例基于庫。 我們知道圖書館的書籍可以借用或歸還,也可以損壞和修理(因此無法借用)。 因此,這正是我們放入模型中的內(nèi)容。
public enum BookStates {AVAILABLE,BORROWED,IN_REPAIR } public enum BookEvents {BORROW,RETURN,START_REPAIR,END_REPAIR }然后,應(yīng)使用以下事務(wù)和狀態(tài)來配置狀態(tài)機(jī):
@Overridepublic void configure(StateMachineStateConfigurer<BookStates, BookEvents> states) throws Exception {states.withStates().initial(BookStates.AVAILABLE).states(EnumSet.allOf(BookStates.class));}@Overridepublic void configure(StateMachineTransitionConfigurer<BookStates, BookEvents> transitions) throws Exception {transitions.withExternal().source(BookStates.AVAILABLE).target(BookStates.BORROWED).event(BookEvents.BORROW).and().withExternal().source(BookStates.BORROWED).target(BookStates.AVAILABLE).event(BookEvents.RETURN).and().withExternal().source(BookStates.AVAILABLE).target(BookStates.IN_REPAIR).event(BookEvents.START_REPAIR).and().withExternal().source(BookStates.IN_REPAIR).target(BookStates.AVAILABLE).event(BookEvents.END_REPAIR); }最后但并非最不重要的一點是,我們允許狀態(tài)機(jī)自動啟動(默認(rèn)情況下不會啟動)。
@Overridepublic void configure(StateMachineConfigurationConfigurer<BookStates, BookEvents> config) throws Exception {config.withConfiguration().autoStartup(true); }現(xiàn)在我們可以在應(yīng)用程序中使用它,看看會發(fā)生什么!
@Overridepublic void run(String... args) {boolean returnAccepted = stateMachine.sendEvent(BookEvents.RETURN);logger.info("return accepted: " + returnAccepted);boolean borrowAccepted = stateMachine.sendEvent(BookEvents.BORROW);logger.info("borrow accepted: " + borrowAccepted); }運(yùn)行應(yīng)用程序時,我們在日志中看到以下內(nèi)容:
2018-07-07 13:46:05.096 INFO 37417 --- [ main] STATE MACHINE : return accepted: false 2018-07-07 13:46:05.098 INFO 37417 --- [ main] STATE MACHINE : borrow accepted: true我故意先打電話給RETURN,看看它會失敗。 但是,它無一例外都失敗:該操作未被接受,并且計算機(jī)保持在AVAILABLE狀態(tài),這使得再次執(zhí)行BORROW成為可能。 那么,如果我們交換兩個呼叫會怎樣?
2018-07-07 13:49:46.218 INFO 37496 --- [ main] STATE MACHINE : borrow accepted: true 2018-07-07 13:49:46.218 INFO 37496 --- [ main] STATE MACHINE : return accepted: true這意味著可以接受正確的交互。 但是,如果我們想更清楚地了解發(fā)生了什么,該怎么辦? 一種方法是為狀態(tài)更改配置處理程序:
@Overridepublic void configure(StateMachineStateConfigurer<BookStates, BookEvents> states) throws Exception {states.withStates().initial(BookStates.AVAILABLE).state(BookStates.AVAILABLE, entryAction(), exitAction()).state(BookStates.BORROWED, entryAction(), exitAction()).state(BookStates.IN_REPAIR, entryAction(), exitAction());}@Beanpublic Action<BookStates, BookEvents> entryAction() {return ctx -> LOGGER.info("Entry action {} to get from {} to {}",ctx.getEvent(),getStateInfo(ctx.getSource()),getStateInfo(ctx.getTarget()));}@Beanpublic Action<BookStates, BookEvents> exitAction() {return ctx -> LOGGER.info("Exit action {} to get from {} to {}",ctx.getEvent(),getStateInfo(ctx.getSource()),getStateInfo(ctx.getTarget())); }2018-07-07 13:53:59.940 INFO 37579 --- [ main] STATE MACHINE : Entry action null to get from EMPTY STATE to AVAILABLE 2018-07-07 13:54:00.051 INFO 37579 --- [ main] STATE MACHINE : return accepted: false 2018-07-07 13:54:00.052 INFO 37579 --- [ main] STATE MACHINE : Exit action BORROW to get from AVAILABLE to BORROWED 2018-07-07 13:54:00.052 INFO 37579 --- [ main] STATE MACHINE : Entry action BORROW to get from AVAILABLE to BORROWED 2018-07-07 13:54:00.053 INFO 37579 --- [ main] STATE MACHINE : borrow accepted: true 2018-07-07 13:54:00.053 INFO 37579 --- [ main] STATE MACHINE : Exit action RETURN to get from BORROWED to AVAILABLE 2018-07-07 13:54:00.053 INFO 37579 --- [ main] STATE MACHINE : Entry action RETURN to get from BORROWED to AVAILABLE 2018-07-07 13:54:00.053 INFO 37579 --- [ main] STATE MACHINE : return accepted: true另一種方法是定義一個成熟的偵聽器:
public class LoggingMashineListener implements StateMachineListener<BookStates, BookEvents> {private static final Logger LOGGER = LoggingUtils.LOGGER;@Overridepublic void stateChanged(State<BookStates, BookEvents> from, State<BookStates, BookEvents> to) {LOGGER.info("State changed from {} to {}", getStateInfo(from), getStateInfo(to));}@Overridepublic void stateEntered(State<BookStates, BookEvents> state) {LOGGER.info("Entered state {}", getStateInfo(state));}@Overridepublic void stateExited(State<BookStates, BookEvents> state) {LOGGER.info("Exited state {}", getStateInfo(state));}@Overridepublic void eventNotAccepted(Message event) {LOGGER.error("Event not accepted: {}", event.getPayload());}@Overridepublic void transition(Transition<BookStates, BookEvents> transition) {// Too much logging spoils the code =)}@Overridepublic void transitionStarted(Transition<BookStates, BookEvents> transition) {// Too much logging spoils the code =)}@Overridepublic void transitionEnded(Transition<BookStates, BookEvents> transition) {// Too much logging spoils the code =)}@Overridepublic void stateMachineStarted(StateMachine<BookStates, BookEvents> stateMachine) {LOGGER.info("Machine started: {}", stateMachine);}@Overridepublic void stateMachineStopped(StateMachine<BookStates, BookEvents> stateMachine) {LOGGER.info("Machine stopped: {}", stateMachine);}@Overridepublic void stateMachineError(StateMachine<BookStates, BookEvents> stateMachine, Exception exception) {LOGGER.error("Machine error: {}", stateMachine);}@Overridepublic void extendedStateChanged(Object key, Object value) {LOGGER.info("Extended state changed: [{}: {}]", key, value);}@Overridepublic void stateContext(StateContext<BookStates, BookEvents> stateContext) {// Too much logging spoils the code =)} }并在配置監(jiān)聽器后將其鏈接到計算機(jī)。 現(xiàn)在我們可以刪除進(jìn)入和退出偵聽器,并且狀態(tài)配置將返回到我們的第一個修訂版(請參見上文)。
@Overridepublic void configure(StateMachineConfigurationConfigurer<BookStates, BookEvents> config) throws Exception {config.withConfiguration().autoStartup(true).listener(new LoggingMashineListener()); }這樣,您將對正在發(fā)生的事情有更多的了解:
2018-07-07 13:59:22.714 INFO 37684 --- [ main] STATE MACHINE : Entered state AVAILABLE 2018-07-07 13:59:22.716 INFO 37684 --- [ main] STATE MACHINE : State changed from EMPTY STATE to AVAILABLE 2018-07-07 13:59:22.717 INFO 37684 --- [ main] STATE MACHINE : Machine started: IN_REPAIR AVAILABLE BORROWED / AVAILABLE / uuid=815f744e-8c5c-4ab1-88d1-b5223199bc4e / id=null 2018-07-07 13:59:22.835 ERROR 37684 --- [ main] STATE MACHINE : Event not accepted: RETURN 2018-07-07 13:59:22.836 INFO 37684 --- [ main] STATE MACHINE : return accepted: false 2018-07-07 13:59:22.837 INFO 37684 --- [ main] STATE MACHINE : Exited state AVAILABLE 2018-07-07 13:59:22.838 INFO 37684 --- [ main] STATE MACHINE : Entered state BORROWED 2018-07-07 13:59:22.838 INFO 37684 --- [ main] STATE MACHINE : State changed from AVAILABLE to BORROWED 2018-07-07 13:59:22.839 INFO 37684 --- [ main] STATE MACHINE : borrow accepted: true 2018-07-07 13:59:22.839 INFO 37684 --- [ main] STATE MACHINE : Exited state BORROWED 2018-07-07 13:59:22.839 INFO 37684 --- [ main] STATE MACHINE : Entered state AVAILABLE 2018-07-07 13:59:22.839 INFO 37684 --- [ main] STATE MACHINE : State changed from BORROWED to AVAILABLE 2018-07-07 13:59:22.839 INFO 37684 --- [ main] STATE MACHINE : return accepted: true什么時候需要狀態(tài)機(jī)? Spring文檔指出,如果滿足以下條件,您已經(jīng)在嘗試實現(xiàn)狀態(tài)機(jī) :
- 使用布爾標(biāo)志或枚舉對情況進(jìn)行建模。
- 具有僅對應(yīng)用程序生命周期的一部分有意義的變量。
- 遍歷if / else結(jié)構(gòu)并檢查是否設(shè)置了特定的標(biāo)志或枚舉,然后進(jìn)一步對當(dāng)標(biāo)志和枚舉的某些組合存在或不存在時的處理方式作進(jìn)一步的例外。
我可以想到一些示例:
- 機(jī)器人 對于狀態(tài)機(jī)來說,這通常是一個很好的例子,因為機(jī)器人通常只有幾個狀態(tài),并且它們之間有不同的動作。 例如,您有一個機(jī)器人在問問題以預(yù)訂酒店(一個著名的例子)。 您會問幾個問題:位置,客人人數(shù),價格范圍等。每個問題都是一個州。 每個答案都是一個事件,可以轉(zhuǎn)換為下一個狀態(tài)。
- 物聯(lián)網(wǎng) 最簡單的狀態(tài)機(jī)具有兩種狀態(tài):ON和OFF。 但是使用比電燈開關(guān)更復(fù)雜的設(shè)備,可能會在它們之間存在更多狀態(tài),并且會有更多事件進(jìn)行狀態(tài)轉(zhuǎn)換。
Spring State Machine可以做的事情還很多。 例如, 狀態(tài)可以嵌套 。 此外,還有可以配置為檢查是否允許過渡的防護(hù)措施 ,以及允許定義選擇狀態(tài),接合狀態(tài)等的 偽狀態(tài)。 事件可以由操作或在計時器上觸發(fā) 。 狀態(tài)機(jī)可以持久化以提高性能。 要瀏覽所有內(nèi)容,您需要研究Spring State Machine文檔并確定適合您的特定情況的文檔。 在這里,我們僅輕輕地刮擦了表面。
您可以觀看有關(guān)Spring State Machine的視頻 ,或研究完整的規(guī)范以了解有關(guān)該主題的更多信息。
可以在這里找到本文的項目源。
翻譯自: https://www.javacodegeeks.com/2018/07/spring-state-machine.html
總結(jié)
以上是生活随笔為你收集整理的Spring State Machine:它是什么,您需要它吗?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gradle文件不识别_识别Gradle
- 下一篇: 三星手电筒怎么开 三星手电筒如何开