日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring event的事件驱动模型的最佳实践@EventListener

發布時間:2024/9/30 编程问答 65 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring event的事件驱动模型的最佳实践@EventListener 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 1.spring下使用event模型
      • 1.1 定義event
      • 1.2 event的監聽處理類。監聽類實現ApplicationListener 里onApplicationEvent方法即可
      • 1.3 發布事件
  • 2.evnet模型的注意點
  • 3. 一種更優雅的方式——@EventListener
      • 3.1 發布事件
      • 3.2 定義事件源
      • 3.3 監聽事件@EventListener
      • 3.4 監聽事件時的事務隔離

? ? ? ?我們知道觀察者模式可以實現代碼的解耦,而spring的event模型就是這種設計模式的極佳體現。一個事件包含:事件發布、監聽、和事件源。在spring中我們可以通過ApplicationContext的publishEvent方法去發布事件;通過實現ApplicationListener接口來自定義自己的監聽器;繼承ApplicationEvent類來實現事件源。下面以一個實例來說明:

1.spring下使用event模型

1.1 定義event

/*** event的基類** @author 94977* @create 2018/7/22*/ public abstract class BaseEvent extends ApplicationEvent {public BaseEvent(Object source) {super(source);} } public class FaceEvent extends BaseEvent {/*** @author 94977* @time 2018/7/22 15:50* @param * @param null* @return* @description 必須要實現的構造方法*/public FaceEvent(User user) {super(user);} }

1.2 event的監聽處理類。監聽類實現ApplicationListener 里onApplicationEvent方法即可

@Component public class FaceEventListener implements ApplicationListener {@Overridepublic void onApplicationEvent(ApplicationEvent event) {if(event instanceof FaceEvent){User user = (User) event.getSource();LOGGER.info("===> 收到人臉事件: {}",user);// .....System.out.println("人臉事件處理結束。。。");}} }

? ? ? ?當然通過event instanceof FaceEvent判斷事件源來處理的方式不是很優雅。有更好的方式,接口ApplicationListener支持泛型,可以通過泛型來判斷處理的事件源。如下只處理FaceEvent源。

@Component public class FaceEventListener extends BaseEventListener implements ApplicationListener<FaceEvent> {@Overridepublic void onApplicationEvent(FaceEvent event) {User user = (User) event.getSource();LOGGER.info("===> 收到人臉事件: {}",user);// .....System.out.println("人臉事件處理結束。。。");} }

如果要實現有序的監聽,實現SmartApplicationListener 接口即可

1.3 發布事件

@Service public class FaceHandler {@Autowiredprivate ApplicationContext applicationContext;public void handle(){User user = new User();user.setAge(34);user.setUsername("人臉事件");user.setHobby("抓拍");//發布事件applicationContext.publishEvent(new FaceEvent(user));//進行其他業務處理}

以上即可。

2.evnet模型的注意點

  • 事件沒要處理的監聽器,就會被拋棄。
  • 一個事件可以同時被多個監聽處理類監聽處理。
  • 以上處理事件都是同步的,如果發布事件處的業務存在事務,監聽器處理也會在相同的事務中。這個一定要注意!如果對于事件的處理不想受到影響,可以onApplicationEvent方法上加@Aync支持異步(參考taskExecutor的使用)。
  • 原理部分可以參考 博客 事件體系

3. 一種更優雅的方式——@EventListener

3.1 發布事件

? ? ? ?我們可以通過工具類發布來避免在代碼耦合注入ApplicationContext,工具類實現ApplicationEventPublisherAware 接口,具體可參考spring的aware學習。
? ? ? ?這里有一個小細節,如果通過注入ApplicationContext的方式來發布事件,idea在代碼左邊會有一個類似耳機的小圖標,點擊可以跳到監聽此發布事件的監聽者位置,用工具類發布事件就沒有此提示了。

3.2 定義事件源

public abstract class BaseEvent<T> extends ApplicationEvent {private static final long serialVersionUID = 895628808370649881L;protected T eventData;public BaseEvent(Object source, T eventData){super(source);this.eventData = eventData;}public BaseEvent(T eventData){super(eventData);}public T getEventData() {return eventData;}public void setEventData(T eventData) {this.eventData = eventData;} }

需要發布的事件繼承此BaseEvent

public class FaceEvent extends BaseEvent<User> {public FaceEvent(User user) {super(user);}public FaceEvent(Object source, User user){super(source,user);}}

如果代碼結構較復雜,多處發布相同的事件,建議發布事件時將this作為source傳遞,便于通過分析日志確定發布源。

3.3 監聽事件@EventListener

? ? ? ?在spring4.2中我們可以以更加簡潔的方式來監聽event的發布,監聽事件我們不必再實現ApplicationListener接口了,只要在方法上添加注解@EventListener即可:

@EventListenerpublic void onApplicationEvent(FaceEvent event) {User user = (User) event.getSource();String name = Thread.currentThread().getName();LOGGER.info("===> 收到人臉事件: {},線程名為: {}",user,name);}

會根據方法參數類型來自動監聽相應事件的發布。
? ? ? ?如果要監聽多個事件類型的發布,可以在@EventListener(classes = {FaceEvent.class,ArmEvent.class})指定,spring會多次調用此方法來處理多個事件。但是注意此時,方法參數不能有多個,否則會發生轉換異常,可以將使用多個事件的父類作為唯一的方法參數來接收處理事件,但除非必要否則并不推薦監聽多個事件的發布。

  • 如果有多個監聽器監聽同一事件,我們可以在方法上使用spring的@order注解來定義多個監聽器的順序,如:
@EventListener@Order(4)public void onApplicationEvent(FaceEvent event) {User user = (User) event.getSource();LOGGER.info("===> A 收到人臉事件: {}",user);}@EventListener({FaceEvent.class,ArmEvent.class})@Order(3)public void onApplicationEvent3(Object event) {if(event instanceof FaceEvent){LOGGER.info("===> B 收到人臉事件: {}",((FaceEvent) event).getEventData());}else if(event instanceof ArmEvent){ArmEvent armEvent = (ArmEvent) event;LOGGER.info("===> B 收到臂膀事件: {}",armEvent.getEventData());}}

這真的是很方便。

  • @EventListener還有一個屬性,condition()里可以使用SPEL表達式來過濾監聽到事件,即只有符合某種條件的才進行接收處理。暫時還用不到。

3.4 監聽事件時的事務隔離

  • @TransactionalEventListener和@EventListener都可以監聽事件,但前者可以對發布事件和監聽事件進行一些事務上的隔離。@TransactionalEventListenerr指不和發布事件的方法在同一個事務內,發布事件的方法事務結束后才會執行本監聽方法,監聽邏輯內發生異常不會回滾發布事件方法的事務。
@Transactional(rollbackFor = Exception.class)public void handle(){User user = new User();user.setAge(34);user.setUsername("人臉事件");user.setHobby("抓拍");//處理完上面的邏輯后,發布事件EventPublisherUtil.publishEvent(new FaceEvent(user));//數據庫添加操作Integer integer = deviceAlarmService.addDevice();}

可以看到發布事件的方法處在事務控制中,我們使用@TransactionalEventListener來監聽事件:

@TransactionalEventListener(fallbackExecution = true)public void onApplicationEvent(FaceEvent event) {User user = event.getEventData();LOGGER.info("===> A 收到人臉事件: {}}",user);//@TransactionalEventListener指不和發布事件的在同一個事務內,發布事件的方法事務結束后才會執行本方法// ,本方法發生異常不會回滾發布事件的事務,throw new RuntimeException("監聽事件拋出異常");}

運行結果,addDevice正常在數據庫插入數據,但是修改為@EventListener監聽則插入數據失敗。

  • @TransactionalEventListener有一個屬性為fallbackExecution,默認為false,指發布事件的方法沒有事務控制時,監聽器不進行監聽事件,此為默認情況! fallbackExecution=true,則指發布事件的方法沒有事務控制時,監聽方法仍可以監聽事件進行處理。
/*** Whether the event should be processed if no transaction is running.*/boolean fallbackExecution() default false;
  • 剛才我們說到使用@TransactionalEventListener會在發布事件的方法事務結束后執行監聽方法,但其實我們還可以進行細化的控制。它有一個屬性為TransactionPhase,默認為TransactionPhase.AFTER_COMMIT,即事務提交后。還可以根據需要選擇AFTER_COMPLETION、BEFORE_COMMIT、AFTER_ROLLBACK。
    ? ? ? ?但仍需注意,如果fallbackExecution=false,且發布事件的方法沒有事務控制時,監聽器根本不會監聽到事件,此處的TransactionPhase也就沒有意義了。

總結

以上是生活随笔為你收集整理的spring event的事件驱动模型的最佳实践@EventListener的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。