Sprievodca projektom Spring State Machine

1. Úvod

Tento článok je zameraný na projekt Spring's State Machine - ktorý je možné použiť na reprezentáciu pracovných tokov alebo akýchkoľvek iných problémov s reprezentáciou automatov s konečným stavom.

2. Závislosť od Maven

Na začiatok musíme pridať hlavnú závislosť Maven:

 org.springframework.statemachine spring-statemachine-core 1.2.3.RELEASE 

Najnovšiu verziu tejto závislosti nájdete tu.

3. Uveďte konfiguráciu stroja

Teraz začnime definovaním jednoduchého stavového automatu:

@Configuration @EnableStateMachine verejná trieda SimpleStateMachineConfiguration rozširuje StateMachineConfigurerAdapter {@Override public void configure (stavy StateMachineStateConfigurer) vyvolá výnimku {stavy .withStates () .initial ("SI") .end ("SF") .states (nový HashSetList (nový HashSetList) ("S1", "S2", "S3"))); } @Override public void configure (StateMachineTransitionConfigurer transitions) vyvolá výnimku {transitions.withExternal () .source ("SI"). Target ("S1"). Event ("E1"). A () .withExternal () .source ( „S1“). Target („S2“). Event („E2“). A () .withExternal () .source („S2“). Target („SF“). Event („end“); }}

Upozorňujeme, že táto trieda je anotovaná ako konvenčná konfigurácia pružiny, ako aj ako stavový automat. Musí sa tiež predĺžiť StateMachineConfigurerAdapter aby bolo možné vyvolať rôzne inicializačné metódy. V jednej z metód konfigurácie definujeme všetky možné stavy stavového automatu, v druhej ako udalosti menia aktuálny stav.

Vyššie uvedená konfigurácia predstavuje celkom jednoduchý lineárny prechodový stavový stroj, ktorého sledovanie by malo byť dosť jednoduché.

Teraz musíme spustiť jarný kontext a získať odkaz na stavový stroj definovaný našou konfiguráciou:

@Autowired private StateMachine stateMachine;

Keď už máme štátny stroj, treba ho spustiť:

stateMachine.start ();

Teraz, keď je náš stroj v počiatočnom stave, môžeme posielať udalosti a tak spúšťať prechody:

stateMachine.sendEvent ("E1");

Vždy môžeme skontrolovať aktuálny stav stavového automatu:

stateMachine.getState ();

4. Akcie

Pridajme niekoľko akcií, ktoré sa majú vykonať okolo prechodov štátov. Najskôr definujeme našu akciu ako jarná fazuľa v rovnakom konfiguračnom súbore:

@Bean public Action initAction () {return ctx -> System.out.println (ctx.getTarget (). GetId ()); }

Potom môžeme vyššie uvedenú akciu s prechodom zaregistrovať v našej konfiguračnej triede:

@Override public void configure (prechody StateMachineTransitionConfigurer) hodí výnimku {transitions.withExternal () transitions.withExternal () .source ("SI"). Target ("S1") .event ("E1"). Action (initAction ())

Táto akcia sa vykoná pri prechode z SI do S1 prostredníctvom udalosti E1 vyskytuje. Akcie možno pripojiť k samotným štátom:

@Bean public Action executeAction () {return ctx -> System.out.println ("Do" + ctx.getTarget (). GetId ()); } uvádza .withStates () .state ("S3", executeAction (), errorAction ());

Táto funkcia definovania stavu prijíma operáciu, ktorá sa má vykonať, keď je stroj v cieľovom stave, a voliteľne obslužnú rutinu chybovej akcie.

Obslužná rutina chybovej akcie sa veľmi nelíši od akejkoľvek inej akcie, ale bude vyvolaná, ak kedykoľvek počas vyhodnotenia akcií štátu dôjde k výnimke:

@Bean public Action errorAction () {return ctx -> System.out.println ("Chyba" + ctx.getSource (). GetId () + ctx.getException ()); }

Je tiež možné zaregistrovať jednotlivé akcie pre vstup, robiť a východ prechody stavu:

@Bean public Action entryAction () {return ctx -> System.out.println ("Záznam" + ctx.getTarget (). GetId ()); } @Bean public Action executeAction () {return ctx -> System.out.println ("Do" + ctx.getTarget (). GetId ()); } @Bean public Action exitAction () {return ctx -> System.out.println ("Exit" + ctx.getSource (). GetId () + "->" + ctx.getTarget (). GetId ()); }
uvádza .withStates () .stateEntry ("S3", entryAction ()) .stateDo ("S3", executeAction ()) .stateExit ("S3", exitAction ());

Príslušné akcie sa vykonajú pri zodpovedajúcich prechodoch stavu. Napríklad by sme mohli chcieť overiť niektoré predbežné podmienky v čase vstupu alebo spustiť nejaké hlásenie v čase výstupu.

5. Globálni poslucháči

Pre stavový automat je možné definovať globálne posluchové udalosti. Na týchto poslucháčov sa bude odvolávať vždy, keď dôjde k prechodu stavu, a možno ich využiť napríklad na prihlásenie alebo zabezpečenie.

Najprv musíme pridať ďalšiu konfiguračnú metódu - takú, ktorá sa nezaoberá stavmi alebo prechodmi, ale konfiguráciou samotného stavového automatu.

Musíme definovať poslucháča rozšírením StateMachineListenerAdapter:

public class StateMachineListener extends StateMachineListenerAdapter {@Override public void stateChanged (State from, State to) {System.out.printf ("Transitioned from% s to% s% n", from == null? "none": from.getId ( ), to.getId ()); }}

Tu sme iba prekabátili stateChanged aj keď je k dispozícii ešte veľa ďalších.

6. Rozšírený stav

Spring State Machine sleduje jeho stav, ale aby sledoval náš žiadosť stav, či už sú to vypočítané hodnoty, záznamy od správcov alebo odpovede z volania na externé systémy, musíme použiť tzv. predĺžený stav.

Predpokladajme, že sa chceme ubezpečiť, že aplikácia účtu prejde dvoma úrovňami schválenia. Môžeme sledovať počet schválení pomocou celého čísla uloženého v rozšírenom stave:

@Bean public Action executeAction () {návrat ctx -> {int schválenia = (int) ctx.getExtendedState (). GetVariables () .getOrDefault ("approvalCount", 0); schválenia ++; ctx.getExtendedState (). getVariables () .put ("approvalCount", schválenia); }; }

7. Stráže

Ochranu je možné použiť na overenie niektorých údajov pred vykonaním prechodu do stavu. Stráž vyzerá veľmi podobne ako akcia:

@Bean public Guard simpleGuard () {return ctx -> (int) ctx.getExtendedState () .getVariables () .getOrDefault ("approvalCount", 0)> 0; }

Tu je badateľný rozdiel, že strážca vráti a pravda alebo nepravdivé ktorý bude informovať štátny automat, či by malo dôjsť k prechodu.

Existuje tiež podpora pre výrazy SPeL ako stráže. Vyššie uvedený príklad mohol byť napísaný ako:

.guardExpression ("extendedState.variables.approvalCount> 0")

8. Stavový stroj od staviteľa

StateMachineBuilder možno použiť na vytvorenie stavového automatu bez použitia jarných anotácií alebo vytvorenia jarného kontextu:

StateMachineBuilder.Builder builder = StateMachineBuilder.builder (); builder.configureStates (). withStates () .initial ("SI") .state ("S1") .end ("SF"); builder.configureTransitions () .withExternal () .source ("SI"). target ("S1"). event ("E1"). a (). withExternal () .source ("S1"). target ("SF" "). udalosť (" E2 "); StateMachine machine = builder.build ();

9. Hierarchické štáty

Hierarchické stavy je možné konfigurovať pomocou viacerých withStates () v súvislosti s rodič ():

uvádza .withStates () .initial ("SI") .state ("SI") .end ("SF"). a () .withStates () .parent ("SI") .initial ("SUB1") .state („SUB2“) .end („SUBEND“);

Tento druh nastavenia umožňuje, aby stavový automat mal viac stavov, takže hovor na getState () vyprodukuje viac ID. Napríklad okamžite po spustení má nasledujúci výraz za následok:

stateMachine.getState (). getIds () ["SI", "SUB1"]

10. Križovatky (možnosti)

Zatiaľ sme vytvorili prechody stavov, ktoré boli svojou podstatou lineárne. Nielen, že je to dosť nezaujímavé, ale tiež to neodráža prípady skutočného použitia, od ktorých bude vývojár požiadaný, aby ich implementovali. Je pravdepodobné, že bude potrebné implementovať podmienené cesty, a práve to nám umožňujú spojenia (alebo voľby) Spring State Machine.

Najskôr musíme v definícii stavu označiť štát križovatkou (voľbou):

uvádza .withStates () .junction ("SJ")

Potom v prechodoch definujeme prvé / potom / posledné možnosti, ktoré zodpovedajú štruktúre ak-potom-iné:

.withJunction () .source ("SJ") .first ("high", highGuard ()) .then ("medium", mediumGuard ()) .last ("low")

najprv a potom vezmime druhý argument, ktorý je pravidelným strážcom a ktorý bude vyvolaný, aby zistil, ktorou cestou sa má vydať:

@Bean public Guard mediumGuard () {return ctx -> false; } @Bean public Guard highGuard () {return ctx -> false; }

Upozorňujeme, že prechod sa nezastaví na spojovacom uzle, ale okamžite vykoná definovaných strážcov a prejde na jednu z určených trás.

Vo vyššie uvedenom príklade bude mať príkaz na prevedenie stavového automatu na SJ za následok skutočný stav nízka pretože obaja strážcovia sa vrátia falošní.

Posledná poznámka je, že API poskytuje spojenia aj možnosti. Funkčne sú však z každého hľadiska identické.

11. Vidlica

Niekedy je potrebné rozdeliť vykonanie na niekoľko nezávislých ciest vykonania. To sa dá dosiahnuť pomocou vidlička funkčnosť.

Najskôr musíme uzol označiť ako uzol vidlice a vytvoriť hierarchické oblasti, do ktorých bude štátny stroj vykonávať rozdelenie:

uvádza .withStates () .initial ("SI"). fork ("SFork"). a () .withStates () .parent ("SFork") .initial ("Sub1-1"). end ("Sub1-2" "). a () .withStates () .parent (" SFork ") .initial (" Sub2-1 ") .end (" Sub2-2 ");

Potom definujte prechod vidlice:

.withFork () .source ("SFork") .target ("Sub1-1") .target ("Sub2-1");

12. Pripojte sa

Doplnkom operácie vidlice je join. Umožňuje nám nastaviť prechodový stav, na ktorý závisí dokončenie niektorých ďalších stavov:

Rovnako ako pri forkingu musíme v definícii stavu určiť uzol spojenia:

uvádza .withStates () .join ("SJoin")

Potom v prechodoch definujeme, ktoré stavy je potrebné dokončiť, aby sme umožnili náš stav spojenia:

prechody .withJoin () .source ("Sub1-2") .source ("Sub2-2") .target ("SJoin");

To je všetko! S touto konfiguráciou, keď oboje Sub1-2 a Sub2-2 ak sa dosiahnu, štátny stroj prejde na Pripojte sa

13. Enums Namiesto Struny

V príkladoch vyššie sme kvôli jasnosti a jednoduchosti použili reťazcové konštanty na definovanie stavov a udalostí. Na produkčnom systéme v reálnom svete by ste pravdepodobne chceli použiť enumy Java, aby ste sa vyhli pravopisným chybám a získali väčšiu bezpečnosť typov.

Najprv musíme definovať všetky možné stavy a udalosti v našom systéme:

public enum ApplicationReviewStates {PEER_REVIEW, PRINCIPAL_REVIEW, APPROVED, REJECTED} verejné enum ApplicationReviewEvents {APPROVE, REJECT}

Po rozšírení konfigurácie musíme tiež odovzdať naše výčty ako všeobecné parametre:

verejná trieda SimpleEnumStateMachineConfiguration rozširuje StateMachineConfigurerAdapter 

Po definovaní môžeme namiesto reťazcov použiť naše konštanty enum. Napríklad na definovanie prechodu:

transitions.withExternal () .source (ApplicationReviewStates.PEER_REVIEW) .target (ApplicationReviewStates.PRINCIPAL_REVIEW) .event (ApplicationReviewEvents.APPROVE)

14. Záver

Tento článok skúmal niektoré funkcie jarného stavového automatu.

Ako vždy nájdete vzorový zdrojový kód na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found