天天看點

Spring 4.2架構中注釋驅動的事件監聽器詳解Spring 4.2架構中注釋驅動的事件監聽器詳解

版權聲明:本文為部落客chszs的原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/chszs/article/details/49097919

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