Spring 4.2框架中註釋驅動的事件監聽器詳解

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項目的樣板代碼,使得代碼更加靈活,尤其是在小數量事件的需求時體現更爲明顯。

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