Spring 4.2框架中註釋驅動的事件監聽器詳解
作者:chszs,版權所有,未經同意,不得轉載。博主主頁:http://blog.csdn.net/chszs
事件交互已經成爲很多應用程序不可或缺的一部分,Spring框架提供了一個完整的基礎設施來處理瞬時事件。下面我們來看看Spring 4.2框架中基於註釋驅動的事件監聽器。
1、早期的方式
在早期,組件要從Spring事件獲知自定義域事件中獲取通知,那麼組件必須實現ApplicationListener接口並覆寫onApplicationEvent方法。
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
上面的代碼工作正常,但是它會針對每一個事件都創建一個新類,從而造成代碼瓶頸。
另外,我們的事件類繼承了ApplicationEvent類——Spring應用中的事件基類。
class OldWayBlogModifiedEvent extends ApplicationEvent {
public OldWayBlogModifiedEvent(Blog blog) {
super(blog);
}
public Blog getBlog() {
return (Blog)getSource();
}
}
請注意,在事件中使用Domain域對象有明顯的缺點,在一些場景下不可用。這裏只是做代碼示例。
順便說一句,ExternalNotificationSender對象負責發送外部通知給已註冊的用戶(例如通過電子郵件、短信等方式)。
2、註釋驅動的事件監聽器
Spring 4.2框架值得注意的一點,用註釋@EventListener註解任意的Spring組件。
@EventListener
public void blogModified(BlogModifiedEvent blogModifiedEvent) {
externalNotificationSender.blogModified(blogModifiedEvent);
}
Spring會爲事件創建一個ApplicationListener實例,並從方法參數中獲取事件的類型。一個類中被事件註釋的方法數量沒有限制,所有相關的事件句柄都會分組到一個類中。
3、有條件的事件處理
爲了使註釋@EventListener的功能更強大,Spring 4.2支持用SpEL表達式表達事件類型的方式。假設以下是事件類:
public class BlogModifiedEvent {
private final Blog blog;
private final boolean importantChange;
public BlogModifiedEvent(Blog blog) {
this(blog, false);
}
public BlogModifiedEvent(Blog blog, boolean importantChange) {
this.blog = blog;
this.importantChange = importantChange;
}
public Blog getBlog() {
return blog;
}
public boolean isImportantChange() {
return importantChange;
}
}
要注意,在實際應用中可能不會有本文這樣的層次結構的事件。
還要注意,用Groovy編寫會更加簡單。
使用條件參數來闡述事件,重要的變化是:
@EventListener(condition = "#blogModifiedEvent.importantChange")
public void blogModifiedSpEL(BlogModifiedEvent blogModifiedEvent) {
externalNotificationSender.blogModifiedSpEL(blogModifiedEvent);
}
4、寬鬆事件類型的層次結構
Spring 4.2之前的版本,ApplicationEventPublisher只有在ApplicationEvent事件後發佈其繼承對象的能力。而在Spring 4.2版開始,此接口已經擴展到支持任意對象類型。在這種情況下,對象被封裝到PayloadApplicationEvent和通過發送。
//base class with Blog field - no need to extend `ApplicationEvent`
class BaseBlogEvent {}
class BlogModifiedEvent extends BaseBlogEvent {}
//somewhere in the code
ApplicationEventPublisher publisher = (...); //injected
publisher.publishEvent(new BlogModifiedEvent(blog)); //just plain instance of the event
這一變化使得發佈事件更容易。然而另一方面它可以導致事件跟蹤變得更加困難,特別是在大型應用程序中。
5、響應發佈事件
註釋@EventListener還有一點需注意,在非空返回類型時,Spring會自動發佈返回的事件。
@EventListener
public BlogModifiedResponseEvent blogModifiedWithResponse(BlogModifiedEvent blogModifiedEvent) {
externalNotificationSender.blogModifiedWithResponse(blogModifiedEvent);
return new BlogModifiedResponseEvent(
blogModifiedEvent.getBlog(), BlogModifiedResponseEvent.Status.OK);
}
6、異步事件處理
註釋@EventListener還可以與註釋@Async進行組合使用,以提供異步事件處理的機制。下面的代碼中,指定的事件監聽器既不會阻塞主要的代碼執行,又不會被其它的監聽器處理。
@Async //Remember to enable asynchronous method execution
//in your application with @EnableAsync
@EventListener
public void blogAddedAsync(BlogAddedEvent blogAddedEvent) {
externalNotificationSender.blogAdded(blogAddedEvent);
}
爲了使工作能夠得到異步執行,通常還需在Spring項目的上下文中使用註釋@EnableAsync。
7、總結
註釋驅動的事件監聽器是Spring框架4.2版中引入的新特性,它減少了Spring項目的樣板代碼,使得代碼更加靈活,尤其是在小數量事件的需求時體現更爲明顯。