Útmutató a Spring State Machine Projecthez

1. Bemutatkozás

Ez a cikk a Spring State Machine projektre összpontosít - amely felhasználható munkafolyamatok vagy bármilyen más véges állapotú automaták reprezentációs problémáinak bemutatására.

2. Maven-függőség

A kezdéshez hozzá kell adnunk a fő Maven-függőséget:

 org.springframework.statemachine spring-statemachine-core 1.2.3. KÖZLEMÉNY 

A függőség legújabb verziója itt található.

3. Állítsa be a gép konfigurációját

Kezdjük egy egyszerű állapotgép definiálásával:

@Configuration @EnableStateMachine nyilvános osztály A SimpleStateMachineConfiguration kiterjeszti a StateMachineConfigurerAdapter {@Orride public void configure (StateMachineStateConfigurer államok) dobja a {Kivétel {állapotokat .withStates () .initial ("SI") .end ("SF H). ("S1", "S2", "S3"))); } @Orride public void configure (StateMachineTransitionConfigurer transitions) dobja a {transzitions.withExternal () .source ("SI"). Target ("S1"). Event ("E1"). És () .withExternal () .source ( "S1"). Target ("S2"). Esemény ("E2"). És () .withExternal () .source ("S2"). Target ("SF"). Esemény ("end"); }}

Ne feledje, hogy ezt az osztályt hagyományos rugókonfigurációként, valamint állapotgépként jegyzik fel. Azt is ki kell terjeszteni StateMachineConfigurerAdapter hogy különféle inicializálási módszereket lehessen használni. Az egyik konfigurációs módszerben meghatározzuk az állapotgép összes lehetséges állapotát, a másikban azt, hogy az események hogyan változtatják meg az aktuális állapotot.

A fenti konfiguráció egy nagyon egyszerű, egyenes vonalú átmeneti állapotgépet határoz meg, amelynek elég könnyen követhetőnek kell lennie.

Most el kell indítanunk egy Spring kontextust, és be kell szereznünk egy hivatkozást a konfigurációnk által definiált állapotgépre:

@Autowired private StateMachine stateMachine;

Miután megvan az államgép, el kell indítani:

stateMachine.start ();

Most, hogy gépünk a kezdeti állapotban van, eseményeket küldhetünk, és ezáltal váltásokat válthatunk ki:

stateMachine.sendEvent ("E1");

Mindig ellenőrizhetjük az állapotgép aktuális állapotát:

stateMachine.getState ();

4. Műveletek

Adjunk hozzá néhány műveletet, amelyeket az állapotátmenetek körül kell végrehajtani. Először tavaszi babként definiáljuk a műveletünket ugyanabban a konfigurációs fájlban:

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

Ezután regisztrálhatjuk a fent létrehozott műveletet az átmenetre a konfigurációs osztályunkban:

A @Orride public void configure (StateMachineTransitionConfigurer átmenetek) kivételt dob ​​a {transitions.withExternal () transitions.withExternal () .source ("SI"). Target ("S1") .event ("E1"). Action (initAction ())

Ez a művelet akkor lesz végrehajtva, amikor áttérünk a SI nak nek S1 eseményen keresztül E1 bekövetkezik. A cselekvések magukhoz az államokhoz köthetők:

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

Ez az állapotdefiníciós funkció elfogadja a végrehajtandó műveletet, amikor a gép célállapotban van, és adott esetben egy hibaművelet-kezelőt.

A hibaműveletek kezelője nem sokban különbözik bármely más művelettől, de akkor hivatkozunk rá, ha az állami cselekvések értékelése során bármikor kivételt vetünk:

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

Lehetőség van az egyes tevékenységek regisztrálására is belépés, csinálni és kijárat állapotátmenetek:

@Bean public Action entryAction () {return ctx -> System.out.println ("Bejegyzés" + 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 ()); }
államok .withStates () .stateEntry ("S3", entryAction ()) .stateDo ("S3", executeAction ()) .stateExit ("S3", exitAction ());

A megfelelő műveleteket a megfelelő állapotátmeneteken hajtják végre. Például érdemes ellenőriznünk néhány előfeltételt a belépéskor, vagy kiváltani néhány jelentést a kilépéskor.

5. Globális hallgatók

Az állapotgépre globális eseményhallgatók határozhatók meg. Ezeket a hallgatókat bármikor meghívják, amikor állapotváltás történik, és felhasználhatók például naplózásra vagy biztonságra.

Először hozzá kell adnunk egy másik konfigurációs módszert - amely nem állapotokkal vagy átmenetekkel foglalkozik, hanem magának az állapotgépnek a konfigurációjával.

Meg kell határozni a hallgatót kiterjesztéssel StateMachineListenerAdapter:

public class StateMachineListener kiterjeszti a 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 ()); }}

Itt csak felülírtuk stateChanged bár sok más horog is rendelkezésre áll.

6. Kiterjesztett állapot

A Spring State Machine nyomon követi az állapotát, de azért, hogy nyomon kövesse az állapotunkat Alkalmazás állapot, legyen az néhány számított érték, adminisztrátorok bejegyzései vagy külső rendszerek hívásának válaszai, használnunk kell az úgynevezett kiterjesztett állapot.

Tegyük fel, hogy meg akarunk bizonyosodni arról, hogy egy fiókalkalmazás két jóváhagyási szinten megy keresztül. A jóváhagyások számát a kibővített állapotban tárolt egész szám segítségével tudjuk nyomon követni:

@Bean public action executeAction () {return ctx -> {int jóváhagyások = (int) ctx.getExtendedState (). GetVariables () .getOrDefault ("jóváhagyási szám", 0); jóváhagyások ++; ctx.getExtendedState (). getVariables () .put ("jóváhagyási szám", jóváhagyások); }; }

7. Őrök

A biztonsági funkcióval bizonyos adatok érvényesíthetők az állapotra való áttérés végrehajtása előtt. Az őr nagyon hasonlít egy cselekvésre:

@Bean public Guard simpleGuard () {return ctx -> (int) ctx.getExtendedState () .getVariables () .getOrDefault ("jóváhagyási szám", 0)> 0; }

Az észrevehető különbség itt az, hogy egy őr visszatér a igaz vagy hamis amely tájékoztatja az államgépet arról, hogy engedélyezni kell-e az átmenetet.

Az SPeL kifejezések támogatása őrként is létezik. A fenti példát a következőképpen is meg lehetett volna írni:

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

8. Állami gép egy építőtől

StateMachineBuilder állapotgép létrehozására használható Spring annotációk vagy Spring kontextus létrehozása nélkül:

StateMachineBuilder.Builder builder = StateMachineBuilder.builder (); builder.configureStates (). withStates () .initial ("SI") .state ("S1") .end ("SF"); builder.configureTransitions () .withExternal () .source ("SI"). target ("S1"). event ("E1") .and (). withExternal () .source ("S1"). target ("SF" ") .event (" E2 "); StateMachine gép = builder.build ();

9. Hierarchikus államok

A hierarchikus állapotok többféle használatával konfigurálhatók withStates () összefüggésben a szülő():

államok .withStates () .initial ("SI") .state ("SI") .end ("SF") .and () .withStates () .parent ("SI") .initial ("SUB1") .state ("SUB2") .end ("SUBEND");

Ez a fajta beállítás lehetővé teszi, hogy az államgépnek több állapota is legyen, ezért hívás getState () több azonosítót fog előállítani. Például közvetlenül az indítás után a következő kifejezés eredményezi:

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

10. Csomópontok (választások)

Eddig olyan állapotátmeneteket hoztunk létre, amelyek természetüknél fogva lineárisak voltak. Nem csak ez meglehetősen érdektelen, de nem tükrözi azokat a valós használati eseteket sem, amelyeket a fejlesztőktől is felkérnek majd. Az esélyek feltételes utakat kell megvalósítani, és a Spring state gép csomópontjai (vagy választásai) lehetővé teszik számunkra, hogy ezt megtegyük.

Először meg kell jelölnünk egy állapotot egy csomópontként (választásként) az állam definíciójában:

állítja .withStates () .junction ("SJ")

Ezután az átmenetekben meghatározzuk az első / majd / utolsó opciókat, amelyek megfelelnek egy if-then-else struktúrának:

.withJunction () .source ("SJ") .első ("high", highGuard ()) .majd ("medium", mediumGuard ()) .last ("low")

első és azután Vegyünk egy második érvet, amely egy rendszeres őr, amelyre hivatkozva kiderül, melyik utat kell választani:

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

Ne feledje, hogy az átmenet nem áll meg egy elágazási csomópontnál, hanem azonnal végrehajtja a meghatározott őröket, és a kijelölt útvonalak egyikére megy.

A fenti példában az állami gép utasítása az SJ-re való áttérésre a tényleges állapot lesz alacsony mivel a két őr csak hamisan tér vissza.

Utolsó megjegyzés az az API csomópontokat és választási lehetőségeket is biztosít. Funkcionálisan azonban minden szempontból azonosak.

11. Villa

Néha szükségessé válik a végrehajtás több független végrehajtási útra osztása. Ez a Villa funkcionalitás.

Először meg kell jelölnünk egy csomópontot villacsomópontként, és hierarchikus régiókat kell létrehoznunk, amelyekre az állapotgép végrehajtja a felosztást:

állítja .withStates () .initial ("SI") .fork ("SFork") .és () .withStates () .parent ("SFork") .initial ("Sub1-1") .end ("Sub1-2" ") .és () .államokkal () .parent (" SFork ") .initial (" Sub2-1 ") .end (" Sub2-2 ");

Ezután határozza meg a villa átmenetét:

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

12. Csatlakozz

A villa működésének kiegészítője a csatlakozás. Ez lehetővé teszi számunkra, hogy olyan állapotot állítsunk át, amelyre más állapotok kitöltése függ:

Csakúgy, mint a villával, az állapotdefinícióban meg kell jelölnünk egy csatlakozási csomópontot:

állítja .withStates () .join ("SJoin")

Ezután az átmenetekben meghatározzuk, mely állapotokat kell teljesítenünk ahhoz, hogy engedélyezzük csatlakozási állapotunkat:

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

Ez az! Ezzel a konfigurációval, amikor mindkettő Sub1-2 és Sub2-2 elérik, az államgép áttér SJoin

13. Enums Ahelyett Húrok

A fenti példákban karakterlánc-állandókat használtunk állapotok és események meghatározásához az érthetőség és az egyszerűség érdekében. Valószínűleg a termelési rendszerben valószínűleg érdemes lenne Java javítóeszközöket használni a helyesírási hibák elkerülése és a nagyobb típusú biztonság érdekében.

Először meg kell határoznunk a rendszerünk összes lehetséges állapotát és eseményét:

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

A konfiguráció kiterjesztésekor általános paraméterként át kell adnunk a számláinkat is:

a SimpleEnumStateMachineConfiguration nyilvános osztály kiterjeszti a StateMachineConfigurerAdaptert 

Miután meghatároztuk, használhatjuk az enum állandókat a húrok helyett. Például egy átmenet meghatározásához:

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

14. Következtetés

Ez a cikk a Spring State gép néhány jellemzőjét tárta fel.

Mint mindig, a forráskód mintáját a GitHubon találja meg.