אביב שעועית מעבד

1. סקירה כללית

אז, במספר מדריכים אחרים, דיברנו עליהם מעבד BeanPost. במדריך זה נשתמש בהם בדוגמה של ממש בעולם באמצעות גויאבה EventBus.

האביב מעבד BeanPost נותן לנו ווים למחזור החיים של שעועית האביב כדי לשנות את תצורתו.

מעבד BeanPost מאפשר שינוי ישיר של השעועית עצמה.

במדריך זה נבחן דוגמה קונקרטית לשיעורים אלה המשלבים שיעורי גויאבה EventBus.

2. התקנה

ראשית, עלינו להגדיר את הסביבה שלנו. בואו להוסיף את ההקשר האביבי, את ביטוי האביב ואת התלות בגויאבה pom.xml:

 org.springframework spring-context 5.2.6.RELEASE org.springframework spring-expression 5.2.6.RELEASE com.google.guava guava 29.0-jre 

לאחר מכן, בואו נדון ביעדים שלנו.

3. יעדים ויישום

למטרה הראשונה שלנו, אנחנו רוצים לנצל את גויאבה EventBus להעביר מסרים על פני היבטים שונים של המערכת באופן אסינכרוני.

לאחר מכן, אנו רוצים לרשום ולבטל רישום של אובייקטים לאירועים באופן אוטומטי בעת יצירת / השמדת שעועית במקום להשתמש בשיטה הידנית שמספקת EventBus.

אז עכשיו אנחנו מוכנים להתחיל בקידוד!

היישום שלנו יורכב משיעור עטיפה לגויאבה EventBus, הערת סמן מותאמת אישית, א מעבד BeanPost, אובייקט מודל, ושעועית לקבלת אירועי סחר במניות מה- EventBus. בנוסף ניצור מקרה מבחן לאימות הפונקציונליות הרצויה.

3.1. EventBus עֲטִיפָה

כדי להיות עם, נגדיר EventBus עטיפה לספק כמה שיטות סטטיות כדי לרשום בקלות ולבטל רישום של שעועית לאירועים שישמשו את מעבד BeanPost:

class final class GlobalEventBus {public static final String GLOBAL_EVENT_BUS_EXPRESSION = "T (com.baeldung.postprocessor.GlobalEventBus) .getEventBus ()"; סופי סטטי פרטי מחרוזת IDENTIFIER = "bus-event-bus"; גמר סטטי פרטי GlobalEventBus GLOBAL_EVENT_BUS = GlobalEventBus חדש (); גמר פרטי EventBus eventBus = AsyncEventBus חדש (IDENTIFIER, Executors.newCachedThreadPool ()); פרטי GlobalEventBus () {} ציבורי סטטי ציבורי GlobalEventBus getInstance () {להחזיר GlobalEventBus.GLOBAL_EVENT_BUS; } ציבור סטטי EventBus getEventBus () {להחזיר GlobalEventBus.GLOBAL_EVENT_BUS.eventBus; } מנוי ריק סטטי ציבורי (Object obj) {getEventBus (). register (obj); } ביטול רישום סטטי ציבורי בטל (Object obj) {getEventBus (). בטל את הרישום (obj); } פוסט ריק סטטי ציבורי (אירוע אובייקט) {getEventBus (). פוסט (אירוע); }}

קוד זה מספק שיטות סטטיות לגישה ל- GlobalEventBus ובבסיס EventBus כמו גם רישום ורישום לאירועים ופרסום אירועים. יש לו גם ביטוי SpEL המשמש כביטוי ברירת המחדל בהערה המותאמת אישית שלנו כדי להגדיר איזה EventBus אנחנו רוצים לנצל.

3.2. ביאור לסמן מותאם אישית

לאחר מכן, נגדיר הערת סמן מותאמת אישית אשר תשמש את ה- מעבד BeanPost כדי לזהות שעועית כדי להירשם / לבטל רישום אוטומטי לאירועים:

@Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) @ הירש ציבורי @interface מנוי {ערך מחרוזת () ברירת מחדל GlobalEventBus.GLOBAL_EVENT_BUS_EXPRESSION; }

3.3. מעבד BeanPost

כעת, נגדיר את מעבד BeanPost אשר יבדוק כל שעועית עבור מָנוּי ביאור. שיעור זה הוא גם א DestructionAwareBeanPostProcessor, שהוא ממשק קפיץ שמוסיף התקשרות לפני ההרס מעבד BeanPost. אם ההערה קיימת, נרשום אותה ב- EventBus מזוהה על ידי ביטוי ה- SpEL של ההערה על אתחול שעועית, ולבטל רישום על השמדת שעועית:

מחלקה ציבורית GuavaEventBusBeanPostProcessor מיישם DestructionAwareBeanPostProcessor {Logger logger = LoggerFactory.getLogger (this.getClass ()); SpelExpressionParser expressionParser = SpelExpressionParser חדש (); @Override ציבור ריק בטל postProcessBeforeDestruction (שעועית אובייקט, שעועית מחרוזת) זורק BeansException {this.process (שעועית, EventBus :: בטל רישום, "הרס"); } @ Override בוליאני ציבורי דורש הרס (שעועית אובייקט) {return true; } @Override אובייקט ציבורי postProcessBeforeInitialization (שעועית אובייקט, שעועית מחרוזת) זורק BeansException {שעועית חזרה; } @Override אובייקט ציבורי postProcessAfterInitialization (שעועית אובייקט, שעועית מחרוזת) זורק BeansException {this.process (שעועית, EventBus :: הרשמה, "אתחול"); שעועית להחזיר; } תהליך בטל פרטי (שעועית אובייקט, צרכן BiConsumer, מחרוזת פעולה) {// ראה הטמעה בהמשך}}

הקוד שלמעלה לוקח כל שעועית ומעביר אותה דרך ה- תהליך שיטה, המוגדרת להלן. הוא מעבד אותו לאחר אתחול השעועית ולפני שהוא נהרס. ה דורש הרס השיטה מחזירה אמת כברירת מחדל ואנו שומרים על התנהגות זו כאן כאשר אנו בודקים את קיומה של ה- @מָנוּי ביאור ב postProcessBeforeDestruction התקשר חזרה.

בואו נסתכל עכשיו על תהליך שיטה:

תהליך בטל פרטי (שעועית אובייקט, צרכן BiConsumer, פעולת מחרוזת) {אובייקט proxy = this.getTargetObject (שעועית); הערת מנויים = AnnotationUtils.getAnnotation (proxy.getClass (), Subscriber.class); אם (הערה == null) להחזיר; this.logger.info ("{}: עיבוד שעועית מסוג {} במהלך {}", this.getClass (). getSimpleName (), proxy.getClass (). getName (), פעולה); AnnotationValue מחרוזת = annotation.value (); נסה {ביטוי ביטוי = this.expressionParser.parseExpression (annotationValue); ערך אובייקט = expression.getValue (); אם (! (מופע ערך של EventBus)) {this.logger.error ("{}: ביטוי {} לא הוערך למופע של EventBus עבור שעועית מסוג {}", this.getClass (). getSimpleName (), annotationValue , proxy.getClass (). getSimpleName ()); לַחֲזוֹר; } EventBus eventBus = (EventBus) ערך; הצרכן. קבלה (eventBus, proxy); } לתפוס (ExpressionException ex) {this.logger.error ("{}: לא מצליח לנתח / להעריך ביטוי {} עבור שעועית מסוג {}", this.getClass (). getSimpleName (), annotationValue, proxy.getClass () .getName ()); }}

קוד זה בודק את קיומה של ההערה המותאמת אישית שלנו לסמן מָנוּי ואם הוא קיים, קורא את ביטוי ה- SpEL מ- ערך תכונה. לאחר מכן, הביטוי מוערך לאובייקט. אם זה מופע של EventBus, אנו מיישמים את BiConsumer פרמטר פונקציה לשעועית. ה BiConsumer משמש לרישום ורישום השעועית מה- EventBus.

יישום השיטה getTargetObject הוא כדלקמן:

אובייקט פרטי getTargetObject (אובייקט proxy) זורק BeansException {אם (AopUtils.isJdkDynamicProxy (proxy)) {נסה {return ((Advised) proxy) .getTargetSource (). getTarget (); } לתפוס (חריג e) {לזרוק FatalBeanException חדש ("שגיאה בהשגת יעד של JDK proxy", e); }} להחזיר פרוקסי; }

3.4. StockTrade אובייקט מודל

לאחר מכן, בואו נגדיר את שלנו סטוק טרייד אובייקט מודל:

מעמד ציבורי StockTrade {סמל מחרוזת פרטי; כמות אינטנס פרטית; מחיר כפול פרטי; תאריך מסחר פרטי תאריך; // בונה}

3.5. StockTradePublisher מקבל אירוע

לאחר מכן, בואו נגדיר כיתת מאזינים שתודיע לנו שהתקבלה עסקה כדי שנוכל לכתוב את המבחן שלנו:

@FunctionalInteface ממשק ציבורי StockTradeListener {void stockTradePublished (StockTrade trade); }

לבסוף נגדיר מקלט חדש סטוק טרייד אירועים:

@ מנוי בכיתה ציבורית StockTradePublisher {Set stockTradeListeners = HashSet new (); ריק ריק addstockTradeListener (מאזין StockTradeListener) {מסונכרן (this.stockTradeListeners) {this.stockTradeListeners.add (מאזין); }} בטל ציבורי להסיר את StockTradeListener (מאזין StockTradeListener) {מסונכרן (this.stockTradeListeners) {this.stockTradeListeners.remove (מאזין); }} @ מנוי @ AllowConcurrentEvents בטל handleNewStockTradeEvent (StockTrade trade) {// פרסם ל- DB, שלח ל- PubNub, ... הגדר מאזינים; מסונכרן (this.stockTradeListeners) {מאזינים = HashSet חדש (this.stockTradeListeners); } מאזינים.forEach (li -> li.stockTradePublished (סחר)); }}

הקוד שלמעלה מסמן מחלקה זו כ- מָנוּי של גויאבה EventBus אירועים וגויאבה @ מנוי ביאור מסמן את השיטה handleNewStockTradeEvent כמקבל אירועים. סוג האירועים שהוא יקבל מבוסס על מחלקת הפרמטר היחיד לשיטה; במקרה זה נקבל אירועים מהסוג סטוק טרייד.

ה @AllowConcurrentEvents ביאור מאפשר הפעלה מקבילה של שיטה זו. ברגע שנקבל עסקה אנו מבצעים כל עיבוד שנרצה ואז נודיע לכל מאזינים.

3.6. בדיקה

עכשיו בואו נסכם את הקידוד שלנו במבחן שילוב כדי לאמת את מעבד BeanPost עובד נכון. ראשית, נצטרך הקשר האביבי:

המחלקה הציבורית @Configuration PostProcessorConfiguration {@Bean הציבור GlobalEventBus eventBus () {להחזיר GlobalEventBus.getInstance (); } @Bean GuavaEventBusBeanPostProcessor eventBusBeanPostProcessor () {להחזיר GuavaEventBusBeanPostProcessor חדש (); } @Bean StockTradePublisher הציבור stockTradePublisher () {להחזיר StockTradePublisher חדש (); }}

כעת אנו יכולים ליישם את הבדיקה שלנו:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = PostProcessorConfiguration.class) מחלקה ציבורית StockTradeIntegrationTest {@Autowired StockTradePublisher stockTradePublisher; @Test הציבור בטל givenValidConfig_whenTradePublished_thenTradeReceived () {תאריך tradeDate = תאריך חדש (); StockTrade stockTrade = StockTrade חדש ("AMZN", 100, 2483.52d, tradeDate); קביעות AtomicBooleanPassed = AtomicBoolean חדש (שקר); מאזין StockTradeListener = סחר -> assertionsPassed .set (this.verifyExact (stockTrade, trade)); this.stockTradePublisher.addStockTradeListener (מאזין); נסה את {GlobalEventBus.post (stockTrade); ממתינים (). atMost (Duration.ofSeconds (2L)) .untilAsserted (() -> assertThat (assertionsPassed.get ()). isTrue ()); } סוף סוף {this.stockTradePublisher.removeStockTradeListener (מאזין); }} אמת בוליאני Exact (StockTrade stockTrade, StockTrade trade) {return Objects.equals (stockTrade.getSymbol (), trade.getSymbol ()) && Objects.equals (stockTrade.getTradeDate (), trade.getTradeDate ()) && stockTrade.getQuantity () == trade.getQuantity () && stockTrade.getPrice () == trade.getPrice (); }}

קוד הבדיקה שלמעלה מייצר סחר במניות ומעביר אותו ל GlobalEventBus. אנו מחכים לכל היותר שתי שניות להשלמת הפעולה ולקבל הודעה על כך שהסחר התקבל על ידי stockTradePublisher. יתר על כן, אנו מאמתים שהסחר שהתקבל לא השתנה במהלך המעבר.

4. מסקנה

לסיכום, האביב מעבד BeanPost מאפשר לנו התאמה אישית של השעועית עצמה, המספק לנו אמצעים לאוטומציה של פעולות שעועית שאחרת נצטרך לעשות באופן ידני.

כמו תמיד, קוד המקור זמין ב- GitHub.