本文主要是簡單的講述了Spring的事件機制,基本概念,講述了事件機制的三要素事件、事件發布、事件監聽器。如何實現一個事件機制,應用的場景,搭配@Async注解實現異步的操作等等。希望對大家有所幫助。
Spring的事件機制是Spring框架中的一個重要特性,基于觀察者模式實現,它可以實現應用程序中的解耦,提高代碼的可維護性和可擴展性。Spring的事件機制包括事件、事件發布、事件監聽器等幾個基本概念。其中,事件是一個抽象的概念,它代表著應用程序中的某個動作或狀態的發生。事件發布是事件發生的地方,它負責產生事件并通知事件監聽器。事件監聽器是事件的接收者,它負責處理事件并執行相應的操作。在Spring的事件機制中,事件源和事件監聽器之間通過事件進行通信,從而實現了模塊之間的解耦。
舉個例子:用戶修改密碼,修改完密碼后需要短信通知用戶,記錄關鍵性日志,等等其他業務操作。
如下圖,就是我們需要調用多個服務來進行實現一個修改密碼的功能。
使用了事件機制后,我們只需要發布一個事件,無需關心其擴展的邏輯,讓我們的事件監聽器去處理,從而實現了模塊之間的解耦。
通過繼承ApplicationEvent,實現自定義事件。是對 Java EventObject 的擴展,表示 Spring 的事件,Spring 中的所有事件都要基于其進行擴展。其源碼如下。
我們可以獲取到timestamp屬性指的是發生時間。
事件發布是事件發生的地方,它負責產生事件并通知事件監聽器。ApplicationEventPublisher用于用于發布 ApplicationEvent 事件,發布后 ApplicationListener 才能監聽到事件進行處理。源碼如下。
需要一個ApplicationEvent,就是我們的事件,來進行發布事件。
ApplicationListener 是 Spring 事件的監聽器,用來接受事件,所有的監聽器都必須實現該接口。該接口源碼如下。
下面會給大家演示如何去使用Spring的事件機制。就拿修改密碼作為演示。
新增一個類,繼承我們的ApplicationEvent。
如下面代碼,繼承后定義了一個userId,有一個UserChangePasswordEvent方法。這里就定義我們監聽器需要的業務參數,監聽器需要那些參數,我們這里就定義那些參數。
/** * @Author JiaQIng * @Description 修改密碼事件 * @ClassName UserChangePasswordEvent * @Date 2023/3/26 13:55 **/@Getter@Setterpublic class UserChangePasswordEvent extends ApplicationEvent { private String userId; public UserChangePasswordEvent(String userId) { super(new Object()); this.userId = userId; }}
實現監聽器有兩種方法:
(1) 新建一個類實現ApplicationListener接口,并且重寫onApplicationEvent方法注入到Spring容器中,交給Spring管理如下代碼新建了一個發送短信監聽器,收到事件后執行業務操作****;
/** * @Author JiaQIng * @Description 發送短信監聽器 * @ClassName MessageListener * @Date 2023/3/26 14:16 **/@Componentpublic class MessageListener implements ApplicationListener<UserChangePasswordEvent> { @Override public void onApplicationEvent(UserChangePasswordEvent event) { System.out.println("收到事件:" + event); System.out.println("開始執行業務操作給用戶發送短信。用戶userId為:" + event.getUserId()); }}
(2) 使用@EventListener注解標注處理事件的方法,此時Spring將創建一個ApplicationListenerbean對象,使用給定的方法處理事件源碼如下參數可以給指定的事件這里巧妙的用到了@AliasFor的能力,放到了@EventListener身上注意:一般建議都需要指定此值,否則默認可以處理所有類型的事件,范圍太廣了;
代碼如下。新建一個事件監聽器,注入到Spring容器中,交給Spring管理。在指定方法上添加@EventListener參數為監聽的事件。方法為業務代碼。使用 @EventListener 注解的好處是一個類可以寫很多監聽器,定向監聽不同的事件,或者同一個事件。
/** * @Author JiaQIng * @Description 事件監聽器 * @ClassName LogListener * @Date 2023/3/26 14:22 **/@Componentpublic class ListenerEvent { @EventListener({ UserChangePasswordEvent.class }) public void LogListener(UserChangePasswordEvent event) { System.out.println("收到事件:" + event); System.out.println("開始執行業務操作生成關鍵日志。用戶userId為:" + event.getUserId()); } @EventListener({ UserChangePasswordEvent.class }) public void messageListener(UserChangePasswordEvent event) { System.out.println("收到事件:" + event); System.out.println("開始執行業務操作給用戶發送短信。用戶userId為:" + event.getUserId()); }}
(3) @TransactionalEventListener來定義一個監聽器,他與@EventListener不同的就是@EventListener標記一個方法作為監聽器,他默認是同步執行,如果發布事件的方法處于事務中,那么事務會在監聽器方法執行完畢之后才提交事件發布之后就由監聽器去處理,而不要影響原有的事務,也就是說希望事務及時提交我們就可以使用該注解來標識注意此注解需要spring-tx的依賴;
注解源碼如下:主要是看一下注釋內容。
// 在這個注解上面有一個注解:@EventListener,所以表明其實這個注解也是個事件監聽器。 @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@EventListenerpublic @interface TransactionalEventListener { /** * 這個注解取值有:BEFORE_COMMIT(指定目標方法在事務commit之前執行)、AFTER_COMMIT(指定目標方法在事務commit之后執行)、 * AFTER_ROLLBACK(指定目標方法在事務rollback之后執行)、AFTER_COMPLETION(指定目標方法在事務完成時執行,這里的完成是指無論事務是成功提交還是事務回滾了) * 各個值都代表什么意思表達什么功能,非常清晰, * 需要注意的是:AFTER_COMMIT + AFTER_COMPLETION是可以同時生效的 * AFTER_ROLLBACK + AFTER_COMPLETION是可以同時生效的 */ TransactionPhase phase() default TransactionPhase.AFTER_COMMIT; /** * 表明若沒有事務的時候,對應的event是否需要執行,默認值為false表示,沒事務就不執行了。 */ boolean fallbackExecution() default false; /** * 這里巧妙的用到了@AliasFor的能力,放到了@EventListener身上 * 注意:一般建議都需要指定此值,否則默認可以處理所有類型的事件,范圍太廣了。 */ @AliasFor(annotation = EventListener.class, attribute = "classes") Class<?>[] value() default {}; /** * The event classes that this listener handles. * <p>If this attribute is specified with a single value, the annotated * method may optionally accept a single parameter. However, if this * attribute is specified with multiple values, the annotated method * must <em>not</em> declare any parameters. */ @AliasFor(annotation = EventListener.class, attribute = "classes") Class<?>[] classes() default {}; /** * Spring Expression Language (SpEL) attribute used for making the event * handling conditional. * <p>The default is {@code ""}, meaning the event is always handled. * @see EventListener#condition */ @AliasFor(annotation = EventListener.class, attribute = "condition") String condition() default ""; /** * An optional identifier for the listener, defaulting to the fully-qualified * signature of the declaring method (e.g. "mypackage.MyClass.myMethod()"). * @since 5.3 * @see EventListener#id * @see TransactionalApplicationListener#getListenerId() */ @AliasFor(annotation = EventListener.class, attribute = "id") String id() default "";}
使用方式如下。phase事務類型,value指定事件。
/** * @Author JiaQIng * @Description 事件監聽器 * @ClassName LogListener * @Date 2023/3/26 14:22 **/@Componentpublic class ListenerEvent { @EventListener({ UserChangePasswordEvent.class }) public void logListener(UserChangePasswordEvent event) { System.out.println("收到事件:" + event); System.out.println("開始執行業務操作生成關鍵日志。用戶userId為:" + event.getUserId()); } @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,value = { UserChangePasswordEvent.class }) public void messageListener(UserChangePasswordEvent event) { System.out.println("收到事件:" + event); System.out.println("開始執行業務操作給用戶發送短信。用戶userId為:" + event.getUserId()); }}
(1) 使用ApplicationContext進行發布,由于ApplicationContext已經繼承了ApplicationEventPublisher,因此可以直接使用發布事件源碼如下;
(2) 直接注入我們的ApplicationEventPublisher,使用@Autowired注入一下;
三種發布事件的方法,我給大家演示一下@Autowired注入的方式發布我們的事件。
@SpringBootTestclass SpirngEventApplicationTests { @Autowired ApplicationEventPublisher appEventPublisher; @Test void contextLoads() { appEventPublisher.publishEvent(new UserChangePasswordEvent("1111111")); }}
我們執行一下看一下接口:
測試成功。
監聽器默認是同步執行的,如果我們想實現異步執行,可以搭配@Async注解使用,但是前提條件是你真的懂@Async注解,使用不當會出現問題的。 后續我會出一篇有關@Async注解使用的文章。這里就不給大家詳細的解釋了。有想了解的同學可以去網上學習一下有關@Async注解使用。
使用@Async時,需要配置線程池,否則用的還是默認的線程池也就是主線程池,線程池使用不當會浪費資源,嚴重的會出現OOM事故。
下圖是阿里巴巴開發手冊的強制要求。
簡單的演示一下:這里聲明一下俺沒有使用線程池,只是簡單的演示一下。
(1) 在我們的啟動類上添加@EnableAsync開啟異步執行配置;
@EnableAsync@SpringBootApplicationpublic class SpirngEventApplication { public static void main(String[] args) { SpringApplication.run(SpirngEventApplication.class, args); }}
(2) 在我們想要異步執行的監聽器上添加@Async注解:
/** * @Author JiaQIng * @Description 事件監聽器 * @ClassName LogListener * @Date 2023/3/26 14:22 **/@Componentpublic class ListenerEvent { @Async @EventListener({ UserChangePasswordEvent.class }) public void logListener(UserChangePasswordEvent event) { System.out.println("收到事件:" + event); System.out.println("開始執行業務操作生成關鍵日志。用戶userId為:" + event.getUserId()); }}
這樣我們的異步執行監聽器的業務操作就完成了。
本文鏈接:http://www.www897cc.com/showinfo-26-10471-0.html使用上 Spring 的事件機制,真香!
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com