阿里大牛親薦:SpringBoot應用程序事件教程,面試Spring你掌握多少

本文轉載自:阿里大牛親薦:SpringBoot應用程序事件教程,面試Spring你掌握多少


如果要“監聽”事件,我們可以在事件發生源處編寫“監聽器”來監聽事件,但會將事件源與偵聽器的邏輯緊密耦合。我們可以根據需要動態註冊和註銷某些事件的偵聽器。對於同一事件,我們也可以有多個偵聽器。本教程概述瞭如何發佈和監聽自定義事件,並解釋了Spring Boot的內置事件。

事件與直接方法調用

事件和直接方法調用都適合於不同的情況。對於方法調用,這就像斷言一樣,無論發送和接收模塊的狀態如何,他們都需要知道此事件的發生。

另一方面,對於事件,我們只是說發生了一個事件,並且通知了哪些模塊不是我們關心的問題。當我們想將處理傳遞給另一個線程時,最好使用事件(例如:在完成某些任務時發送電子郵件)。同樣,事件對於測試驅動的開發非常有用。

事件用於在鬆耦合的組件之間交換信息。由於發佈者和訂閱者之間沒有直接耦合,因此我們可以修改訂閱者而不影響發佈者,反之亦然。讓我們看看如何在Spring Boot應用程序中創建,發佈和收聽自定義事件。

1. 創建一個 ApplicationEvent

我們可以使用Spring Framework的事件發佈機制來發布應用程序事件。

讓我們創建一個UserCreatedEvent通過擴展調用的自定義事件ApplicationEvent:

class UserCreatedEvent extends ApplicationEvent {
	private String name;
	UserCreatedEvent(Object source, String name) {
		super(source);
		this.name = name;
	}
	...
}

source對象是事件發生時可以初始化和傳遞的參數,傳遞道super()方法。

從Spring 4.2開始,我們還可以將對象直接發佈爲事件,而無需擴展ApplicationEvent:

class UserRemovedEvent {
	private String name;
	UserRemovedEvent(String name) {
		this.name = name;
	}
	...
}

2.發佈一個 ApplicationEvent

我們使用ApplicationEventPublisher接口來發布事件:

@Component
class Publisher {
	private final ApplicationEventPublisher publisher;
	Publisher(ApplicationEventPublisher publisher) {
		this.publisher = publisher;
	}
	void publishEvent(final String name) {
		// Publishing event created by extending ApplicationEvent
		publisher.publishEvent(new UserCreatedEvent(this, name));
		// Publishing an object as an event
		publisher.publishEvent(new UserRemovedEvent(name));
	}
}

當我們發佈的對象不是ApplicationEvent時,Spring會自動用PayloadApplicationEvent包裝它

3. 監聽事件

現在我們知道如何創建和發佈自定義事件,讓我們看看如何監聽事件。一個事件可以有多個偵聽器根據應用程序需求執行不同的工作。

有兩種定義偵聽器的方法。我們可以使用@EventListener註釋或實現ApplicationListener接口。無論哪種情況,監聽器類都必須由Spring管理。

從Spring 4.1開始,現在可以簡單地註釋託管bean的方法,@EventListener以自動註冊ApplicationListener與該方法的簽名匹配的方法:

@Component
class UserRemovedListener {
	@EventListener
	  ReturnedEvent handleUserRemovedEvent(UserRemovedEvent event) {
		// handle UserRemovedEvent ...
		return new ReturnedEvent();
	}
	@EventListener
	  void handleReturnedEvent(ReturnedEvent event) {
		// handle ReturnedEvent ...
	}
	...
}

啓用註釋驅動的配置時,不需要其他配置。我們的方法可以監聽多個事件,或者如果我們想完全不使用任何參數來定義它,那麼事件類型也可以在註釋本身上指定。範例:@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})。

對於帶有註釋@EventListener的方法的返回類型如定義爲非void,Spring會將結果作爲新事件發佈給我們。在上面的示例中,ReturnedEvent第一種方法返回的結果將被髮布,然後由第二種方法處理。

如果指定SpEL,Spring僅在某些情況下允許觸發我們的偵聽器condition:

@Component
class UserRemovedListener {
	@EventListener(condition = "#event.name eq 'reflectoring'")
	  void handleConditionalListener(UserRemovedEvent event) {
		// handle UserRemovedEvent
	}
}

僅當表達式的計算結果爲true,或包含以下字符串之一時:“true”, “on”, “yes”, 或“1”.方法參數通過其名稱公開。條件表達式還公開了一個引用了raw ApplicationEvent(#root.event)和實際方法參數的“根”變量(#root.args)

在以上示例中,UserRemovedEvent僅當#event.name的值爲時’reflectoring’,纔會觸發偵聽器。

偵聽事件的另一種方法是實現ApplicationListener接口:

@Component
class UserCreatedListener implements ApplicationListener<UserCreatedEvent> {
	@Override
	  public void onApplicationEvent(UserCreatedEvent event) {
		// handle UserCreatedEvent
	}
}

只要偵聽器對象在Spring應用程序上下文中註冊,它就會接收事件。當Spring路由一個事件時,它使用偵聽器的簽名來確定它是否與事件匹配。

異步事件監聽器

默認情況下,spring事件是同步的,這意味着發佈者線程將阻塞,直到所有偵聽器都完成對事件的處理爲止。

要使事件偵聽器以異步模式運行,我們要做的就是@Async在該偵聽器上使用註釋:

@Component
class AsyncListener {
	@Async
	  @EventListener
	  void handleAsyncEvent(String event) {
		// handle event
	}
}

爲了使@Async註釋生效,我們還必須註釋一個@Configuration類,使用@EnableAsync註釋SpringBootApplication類。

上面的代碼示例還顯示,我們可以將String用作事件。使用風險自負。最好使用特定於我們用例的數據類型,以免與其他事件衝突。

事務綁定事件

Spring允許我們將事件偵聽器綁定到當前事務的某個階段。噹噹前事務的結果對偵聽器很重要時,這使事件可以更靈活地使用。

當我們使用註釋我們的方法時@TransactionalEventListener,我們得到了一個擴展的事件監聽器,該監聽器知道事務:

@Component
class UserRemovedListener {
	@TransactionalEventListener(phase=TransactionPhase.AFTER_COMPLETION)
	  void handleAfterUserRemoved(UserRemovedEvent event) {
		// handle UserRemovedEvent
	}
}

UserRemovedListener 僅在當前事務完成時才調用。

我們可以將偵聽器綁定到事務的以下階段:

  • AFTER_COMMIT:成功提交事務後,將處理該事件。如果事件偵聽器僅在當前事務成功時才運行,則可以使用此方法。
  • AFTER_COMPLETION:在事務提交或回滾時將處理該事件。例如,我們可以使用它在事務完成後執行清理。
  • AFTER_ROLLBACK:交易回滾後,將處理該事件。
  • BEFORE_COMMIT:事件將在事務提交之前處理。例如,我們可以使用它來將事務性O / R映射會話刷新到數據庫。

Spring Boot的應用程序事件

以上是Spring事件,Spring Boot提供了幾個預定義ApplicationEvent的,這些預定義綁定到SpringApplication生命週期。

在ApplicationContext創建之前會觸發一些事件,因此我們無法將這些事件註冊爲@Bean。我們可以通過手動添加偵聽器來註冊這些事件的偵聽器:

@SpringBootApplication
public class EventsDemoApplication {
	public static void main(String[] args) {
		SpringApplication springApplication = 
		        new SpringApplication(EventsDemoApplication.class);
		springApplication.addListeners(new SpringBuiltInEventsListener());
		springApplication.run(args);
	}
}

通過將META-INF/spring.factories文件添加到我們的項目中,我們還可以註冊偵聽器,而不管如何創建應用的。並通過以下org.springframework.context.ApplicationListener鍵引用偵聽器:

org.springframework.context.ApplicationListener= com.reflectoring.eventdemo.SpringBuiltInEventsListener
class SpringBuiltInEventsListener 
    implements ApplicationListener<SpringApplicationEvent>{
  @Override
  public void onApplicationEvent(SpringApplicationEvent event) {
	// handle event
}
}

一旦確保正確註冊了事件監聽器,我們就可以監聽所有Spring Boot的SpringApplicationEvents。讓我們按照它們應用程序啓動期間的執行順序來看看:

①. ApplicationStartingEvent

ApplicationStartingEvent在運行開始時但在任何處理之前都會觸發,除了偵聽器和初始化程序的註冊外。

②. ApplicationEnvironmentPreparedEvent

當Environment在上下文中是可用的,一個ApplicationEnvironmentPreparedEvent被觸發,由於此時Environment將準備就緒,因此我們可以在其他bean使用它之前對其進行檢查和修改。

③. ApplicationContextInitializedEvent

ApplicationContext已準備就緒時,一個ApplicationContextInitializedEvent觸發,ApplicationContextInitializers被稱爲尚未加載bean定義。在bean初始化到Spring容器之前,我們可以使用它執行任務。

④. ApplicationPreparedEvent

當ApllicationContext準備就緒時,一個ApplicationPreparedEvent時會觸發,但不會刷新。

在準備好的Environment和bean定義將被加載。

⑤. ContextRefreshedEvent

當ApplicationContext刷新時,ContextRefreshedEvent會觸發。

ContextRefreshedEvent是直接來自Spring,而不是Spring Boot,並不繼承擴展SpringApplicationEvent。

⑥. WebServerInitializedEvent

如果我們使用的是Web服務器,WebServerInitializedEvent則在Web服務器準備就緒後會觸發a。ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分別是servlet和反應式變量。

WebServerInitializedEvent不是繼承擴展SpringApplicationEvent。

⑦. ApplicationStartedEvent

上下文已被刷新之後,一個ApplicationStartedEvent觸發,但在任何Spring boot應用程序和命令行運行都被調用前。

⑧. ApplicationReadyEvent

一個ApplicationReadyEvent觸發時就表示該應用程序已準備好服務請求。

建議此時不要修改內部狀態,因爲所有初始化步驟都將完成。

⑨. ApplicationFailedEvent

一個ApplicationFailedEvent如果有異常,應用程序無法啓動點火。在啓動期間的任何時間都可能發生這種情況。我們可以使用它來執行一些任務,例如執行腳本或在啓動失敗時發出通知。

結論

事件被設計爲在同一應用程序上下文中在Spring bean之間進行簡單的通信。從Spring 4.2開始,基礎結構已得到顯着改進,並提供了基於註釋的模型以及發佈任意事件的功能


本文轉載自:阿里大牛親薦:SpringBoot應用程序事件教程,面試Spring你掌握多少

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章