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還有一個屬性,condition()里可以使用SPEL表達式來過濾監聽到事件,即只有符合某種條件的才進行接收處理。暫時還用不到。
3.4 監聽事件時的事務隔離
- @TransactionalEventListener和@EventListener都可以監聽事件,但前者可以對發布事件和監聽事件進行一些事務上的隔離。@TransactionalEventListenerr指不和發布事件的方法在同一個事務內,發布事件的方法事務結束后才會執行本監聽方法,監聽邏輯內發生異常不會回滾發布事件方法的事務。
可以看到發布事件的方法處在事務控制中,我們使用@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,則指發布事件的方法沒有事務控制時,監聽方法仍可以監聽事件進行處理。
- 剛才我們說到使用@TransactionalEventListener會在發布事件的方法事務結束后執行監聽方法,但其實我們還可以進行細化的控制。它有一個屬性為TransactionPhase,默認為TransactionPhase.AFTER_COMMIT,即事務提交后。還可以根據需要選擇AFTER_COMPLETION、BEFORE_COMMIT、AFTER_ROLLBACK。
? ? ? ?但仍需注意,如果fallbackExecution=false,且發布事件的方法沒有事務控制時,監聽器根本不會監聽到事件,此處的TransactionPhase也就沒有意義了。
總結
以上是生活随笔為你收集整理的spring event的事件驱动模型的最佳实践@EventListener的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面向切面编程AOP的最佳入门示例
- 下一篇: Tomcat入门