Tavaszi BeanPostProcessor

1. Áttekintés

Tehát számos más oktatóanyagban beszéltünk róla BeanPostProcessor. Ebben az oktatóanyagban felhasználjuk őket egy valós példa felhasználására Guava használatával EventBus.

Tavaszi BeanPostProcessor horgokat ad a tavaszi bab életciklusába, hogy módosítsuk a konfigurációját.

BeanPostProcessor lehetővé teszi maguk a bab közvetlen módosítását.

Ebben az oktatóanyagban egy konkrét példát fogunk megvizsgálni ezeken az osztályokon, amelyek integrálják a Guava-t EventBus.

2. Beállítás

Először fel kell állítanunk a környezetünket. Vegyük hozzá a tavaszi kontextus, a tavaszi kifejezés és a guava függőségeket pom.xml:

 org.springframework spring-context 5.2.6.RELEASE org.springframework tavaszi kifejezés 5.2.6.RELEASE com.google.guava guava 29.0-jre 

Ezután beszéljük meg céljainkat.

3. Célok és megvalósítás

Az első célunkhoz szeretnénk felhasználják a Guava-okat EventBus aszinkron módon továbbítani az üzeneteket a rendszer különböző aspektusain keresztül.

Ezután az objektumok eseményeinek automatikus regisztrálását és regisztrációjának törlését a bab létrehozásán / megsemmisítésén szeretnénk végrehajtani, ahelyett, hogy az általam biztosított kézi módszert alkalmaznánk EventBus.

Tehát készen állunk a kódolás megkezdésére!

Megvalósításunk a Guava-i burkoló osztályból áll EventBus, egyedi jelölőjegyzet, a BeanPostProcessor, egy minta objektumot és egy babot, hogy tőzsdei eseményeket fogadjon a EventBus. Ezenkívül létrehozunk egy tesztesetet a kívánt funkcionalitás ellenőrzésére.

3.1. EventBus Csomagolás

Ahhoz, hogy együtt legyünk, meghatározunk egy EventBus csomagoló, amely statikus módszereket kínál a bab egyszerű regisztrálására és regisztrációjának törlésére az események során, amelyeket a BeanPostProcessor:

public final class GlobalEventBus {public static final String GLOBAL_EVENT_BUS_EXPRESSION = "T (com.baeldung.postprocessor.GlobalEventBus) .getEventBus ()"; privát statikus végső karakterlánc azonosító = "globális esemény-busz"; privát statikus végleges GlobalEventBus GLOBAL_EVENT_BUS = új GlobalEventBus (); private final EventBus eventBus = new AsyncEventBus (IDENTIFIER, Executors.newCachedThreadPool ()); privát GlobalEventBus () {} nyilvános statikus GlobalEventBus getInstance () {return GlobalEventBus.GLOBAL_EVENT_BUS; } public static EventBus getEventBus () {return GlobalEventBus.GLOBAL_EVENT_BUS.eventBus; } public static void subscribe (Object obj) {getEventBus (). register (obj); } public static void unsubscribe (Object obj) {getEventBus (). unregister (obj); } public static void post (Object event) {getEventBus (). post (esemény); }}

Ez a kód statikus módszereket biztosít a GlobalEventBus és mögöttes EventBus valamint eseményekre történő regisztráció és regisztráció megszüntetése, valamint események kifüggesztése. Ezenkívül rendelkezik egy SpEL kifejezéssel, amelyet alapértelmezett kifejezésként használunk az egyedi kommentárunkban annak meghatározására EventBus szeretnénk hasznosítani.

3.2. Egyéni jelölő kommentár

Ezután definiáljunk egy egyéni jelölőjegyzetet, amelyet a BeanPostProcessor babok azonosítása az események automatikus regisztrálásához / regisztrációjának megszüntetéséhez:

@Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) @ Örökölt nyilvános @interface előfizető {String value () alapértelmezett GlobalEventBus.GLOBAL_EVENT_BUS_EXPRESSION; }

3.3. BeanPostProcessor

Most meghatározzuk a BeanPostProcessor amely minden babot ellenőriz Előfizető annotáció. Ez az osztály is a DestructionAwareBeanPostProcessor, amely egy Spring interfész, amely egy rombolás előtti visszahívást ad hozzá BeanPostProcessor. Ha a megjegyzés jelen van, regisztráljuk a EventBus amelyet az annotáció a bab inicializálására vonatkozó SpEL kifejezéssel azonosított, és a bab megsemmisítésénél törölte a regisztrációt:

public class GuavaEventBusBeanPostProcessor implementates DestructionAwareBeanPostProcessor {Logger logger = LoggerFactory.getLogger (this.getClass ()); SpelExpressionParser expressionParser = new SpelExpressionParser (); @Orride public void postProcessBeforeDestruction (Object bean, String beanName) dobja BeansException {this.process (bean, EventBus :: regisztráció, "megsemmisítés"); } @Orride public boolean needsDestruction (Object bean) {return true; } @Orride public Object postProcessBeforeInitialization (Object bean, String beanName) dobja a BeansException {return bean; } @Orride public Object postProcessAfterInitialization (Object bean, String beanName) dobja a BeansException {this.process (bean, EventBus :: register, "inicializálás"); visszatérő bab; } private void process (Object bean, BiConsumer consumer, String action) {// A megvalósítást lásd alább}}

A fenti kód minden babot felvesz, és végigfuttatja a folyamat módszer, amelyet az alábbiakban határozunk meg. A bab inicializálása és megsemmisítése után dolgozza fel. A megsemmisítést igényel A metódus alapértelmezés szerint true-val tér vissza, és ezt a viselkedést itt tartjuk, amikor ellenőrizzük a @Előfizető annotáció a postProcessBeforeDestruction visszahív.

Most nézzük meg a folyamat módszer:

private void folyamat (Object bean, BiConsumer fogyasztó, String művelet) {Object proxy = this.getTargetObject (bean); Előfizetői kommentár = AnnotationUtils.getAnnotation (proxy.getClass (), Subscriber.class); if (annotáció == null) return; this.logger.info ("{}: a (z) {} típusú bab feldolgozása a {} során", this.getClass (). getSimpleName (), proxy.getClass (). getName (), művelet); String annotationValue = annotation.value (); próbáld ki az {Expression express = this.expressionParser.parseExpression (annotationValue); Objektum értéke = kifejezés.getValue (); if (! (EventBus érték példánya)) {this.logger.error ("" {}: kifejezés {} nem értékelte az EventBus egy példányát a (z) {} típusú bab esetében, ez.getClass (). getSimpleName (), annotationValue , proxy.getClass (). getSimpleName ()); Visszatérés; } EventBus eventBus = (EventBus) érték; fogyasztó.fogadás (eventBus, proxy); } catch (ExpressionException ex) {this.logger.error ("{}: nem lehet elemezni / kiértékelni a (z) {} kifejezést a (z) {} típusú babra, ez.getClass (). getSimpleName (), annotationValue, proxy.getClass () .getName ()); }}

Ez a kód ellenőrzi, hogy létezik-e megnevezett egyedi jelölőnk Előfizető és ha van, kiolvassa a SpEL kifejezést abból érték ingatlan. Ezután a kifejezést objektummá értékelik. Ha ez a példa EventBus, alkalmazzuk a BiConsumer függvény paraméter a babnak. A BiConsumer a bab bejegyzésére és a regisztráció törlésére szolgál EventBus.

A módszer megvalósítása getTargetObject az alábbiak:

magánobjektum getTargetObject (Object proxy) dobja a BeansException {if (AopUtils.isJdkDynamicProxy (proxy)) {try {return ((tanácsos) proxy) .getTargetSource (). getTarget (); } catch (e kivétel) {dob új FatalBeanException ("Hiba a JDK proxy célpontjának lekérésekor", e); }} return proxy; }

3.4. StockTrade Modellobjektum

Ezután határozzuk meg a sajátunkat StockTrade modell objektum:

public class StockTrade {private String szimbólum; privát int mennyiség; magán dupla ár; private Date tradeDate; // konstruktor}

3.5. StockTradePublisher Esemény vevő

Ezután határozzunk meg egy hallgatói osztályt, hogy értesítsen minket arról, hogy érkezett egy ügylet, hogy megírhassuk a tesztünket:

@FunctionalInterface nyilvános felület StockTradeListener {void stockTradePublished (StockTrade kereskedelem); }

Végül meghatározzuk az új vevõjét StockTrade események:

@Subscriber public class StockTradePublisher {Set stockTradeListeners = new HashSet (); public void addStockTradeListener (StockTradeListener hallgató) {szinkronizált (this.stockTradeListeners) {this.stockTradeListeners.add (hallgató); }} public void removeStockTradeListener (StockTradeListener hallgató) {szinkronizált (this.stockTradeListeners) {this.stockTradeListeners.remove (hallgató); }} @Subscribe @AllowConcurrentEvents void handleNewStockTradeEvent (StockTrade kereskedelem) {// közzététel DB-be, küldés a PubNub-ba, ... Állítsa be a hallgatókat; szinkronizált (this.stockTradeListeners) {hallgatók = new HashSet (this.stockTradeListeners); } hallgatók.forEach (li -> li.stockTradePublished (kereskedelem)); }}

A fenti kód ezt az osztályt a-ként jelöli Előfizető Guava EventBus események és Guava-k @Iratkozz fel annotáció jelöli a módszert handleNewStockTradeEvent mint események befogadója. A fogadott események típusa a módszer egyetlen paraméterének osztályán alapul; ebben az esetben típusú eseményeket fogunk fogadni StockTrade.

A @AllowConcurrentEvents az annotáció lehetővé teszi a módszer egyidejű meghívását. Amint megkaptuk a kereskedést, bármilyen feldolgozást végzünk, amelyet kívánunk, majd értesítjük a hallgatókat.

3.6. Tesztelés

Most fejezzük be kódolásunkat egy integrációs teszttel a BeanPostProcessor megfelelően működik. Először is szükségünk lesz egy tavaszi kontextusra:

@Configuration public class PostProcessorConfiguration {@Bean public GlobalEventBus eventBus () {return GlobalEventBus.getInstance (); } @Bean public GuavaEventBusBeanPostProcessor eventBusBeanPostProcessor () {return new GuavaEventBusBeanPostProcessor (); } @Bean public StockTradePublisher stockTradePublisher () {return new StockTradePublisher (); }}

Most végre tudjuk hajtani tesztünket:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (class = PostProcessorConfiguration.class) public class StockTradeIntegrationTest {@Autowired StockTradePublisher stockTradePublisher; @Test public void givenValidConfig_whenTradePublished_thenTradeReceived () {Date tradeDate = new Date (); StockTrade stockTrade = new StockTrade ("AMZN", 100, 2483.52d, tradeDate); AtomicBoolean assertionsPassed = új AtomicBoolean (hamis); StockTradeListener hallgató = kereskedelem -> assertionsPassed .set (this.verifyExact (stockTrade, kereskedelem)); this.stockTradePublisher.addStockTradeListener (figyelő); próbáld ki a {GlobalEventBus.post (stockTrade); wait (). atMost (Duration.ofSeconds (2L)) .untilAsserted (() -> assertThat (assertionsPassed.get ()). isTrue ()); } végül {this.stockTradePublisher.removeStockTradeListener (hallgató); }} logikai igazolásExact (StockTrade stockTrade, StockTrade trade) {return Objects.equals (stockTrade.getSymbol (), trade.getSymbol ()) && Objects.equals (stockTrade.getTradeDate (), trade.getTradeDate ()) && stockTrade.getQu () == kereskedelem.getQuantity () && stockTrade.getPrice () == kereskedelem.getPrice (); }}

A fenti tesztkód tőzsdei kereskedelmet generál, és azt a GlobalEventBus. Legfeljebb két másodpercet várunk, amíg a művelet befejeződik, és értesítést kapunk a kereskedelemről stockTradePublisher. Továbbá érvényesítjük, hogy a beérkezett ügyleteket nem módosították szállítás közben.

4. Következtetés

Összegzésképpen: Tavasz BeanPostProcessor lehetővé teszi számunkra személyre szabhatja a babot, biztosítva számunkra a babos műveletek automatizálására szolgáló eszközöket, amelyeket egyébként manuálisan kellene elvégeznünk.

Mint mindig, a forráskód is elérhető a GitHubon.