目前的版本是spring 4.3.10 release版,這裏的@Transcational註解是spring的註解,而不是javax.persitence的註解html
通常而言,事件的生命週期爲事件建立, 事件發佈,事件訂閱,事件結束.java
spring的事件都繼承ApplicationEvent,而後添加本身須要的東西spring
以下:數組
public class LoginEvent extends ApplicationEvent implements PointCommon { public LoginEvent(Object source) { super(source); } private String activeId; private String activeCode="login"; private Boolean pointFlag; public String getActiveId() { return activeId; } public void setActiveId(String activeId) { this.activeId = activeId; } @Override public String getActiveCode() { return activeCode; } public Boolean getPointFlag() { return pointFlag; } public void setPointFlag(Boolean pointFlag) { this.pointFlag = pointFlag; } public LoginEvent(Object source, String activeId, String activeCode, Boolean pointFlag) { super(source); this.activeId = activeId; this.pointFlag = pointFlag; } }
這是一個自定的登陸事件,定義了基本的事件信息.架構
通常實現ApplicationEventPublisherAware,使用 ApplicationEventPublisher publisher來發布事件. 自定義事件app
public class LoginService implements ApplicationEventPublisherAware { private EventService eventService; private ApplicationEventPublisher eventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.eventPublisher = applicationEventPublisher; } public void login(HttpServletRequest request...){ LoginEvent event = new LoginEvent("", "234sd", "login", true); publisher.publishEvent(event); } }
實現ApplicationListener<T>接口,T可指定事件的類型,可是要繼承ApplicationEvent才行;異步
@Component public class PointHandler implements ApplicationListener<LoginEvent >{ @Override public void onApplicationEvent(LoginEvent event) { ... } }
這樣就能夠處理事件了.jvm
與下面的方式相比,繼承EventListener比較笨重.async
@EventListener public void handle(PointCommon event) { out.println("----斯蒂芬-----"); out.println(event.getDescription()); }
沒錯,直接加上@EventListener就能實現了監聽器的功能.ide
事件鏈中同步事件方法出現異常,整個事件鏈就會出現中斷.不繼續執行.異步方法無影響.
當全部的時間監聽器處理完事件以後,事件纔算結束,jvm纔會執行其餘操做.但有異步事件監聽器時,只要全部的同步事件監聽器處理完事件以後,jvm就會執行其餘操做,且異步事件不會對事件源方法產生有效影響.這裏能夠認爲事件已經結束了.
咱們能夠經過註解的condition屬性來添加額外的運行時攔截條件,該屬性定義了一個SpEL表達式,你須要匹配這個特定事件,才能調用相應的方法.
例如,咱們的通知器能夠重寫爲只有當事件的test屬性等於foo時纔會調用.
@EventListener(condition = "#event.test == 'foo'") public void processBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress... }
每一次SpEL表達式評估都有專門的上下文.下面的表格展現了你在該上下文中能得到的選項,全部你可使用他們進行有條件的事件處理:
名稱:event 位置:root object 描述:實際的ApplicationEvent(應用事件),例子:#root.event
名稱:args 位置:root Object 描述:調用目標使用的參數(數組) 例子:#root.args[0]
名稱:argument name 位置:evaluation context 描述:方法中每一個參數的名字.若是因某些緣由這些名字不可得到,這個參數的名稱仍能夠經過#a<#arg>來獲取,這個#arg表明的是參數的下標(從0開始). 例子:#iban或#a0(也可使用#p0或使用#p<#arg>標誌做爲別名)
記住#root.event容許你訪問對應的事件,即便你的方法簽名實際上對應的是任意要發佈的對象.
若是你由於處理完某個事件後須要發佈一個新事件,只須要把方法簽名的返回修改成你須要發佈的事件的類型,像這樣:
@EventListener public ListUpdateEvent handleBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress and // then publish a ListUpdateEvent... }
該功能不支持異步的監聽器;
這個新方法會爲每一個該方法處理過的blackListEvent發佈一個新的ListUpdateEvent事件.若是你須要發佈幾個事件,只須要返回這些事件的集合.
異步事件處理方法不支持該特性.
若是你想要一個特別的監聽器來異步處理事件,簡單的使用常規的@Async支持:
@EventListener @Async public void processBlackListEvent(BlackListEvent event) { // BlackListEvent is processed in a separate thread }
記住使用異步事件時注意如下幾個侷限:
若是你想要一個監聽器先於其餘調用.只須要在方法參數上添加@Order註解;
@EventListener @Order(42) public void processBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress... }
你可使用泛型更深刻的定義你的事件的結構.思考一個EntityCreatedEvent<T>,其中T是被建立實體的實際類型.你能夠建立一個只用來接受Person類的EntityCreatedEvent事件監聽器;
@EventListener public void onPersonCreated(EntityCreatedEvent<Person> event) { ... }
因爲類型擦除,只有當事件無缺符合事件監聽器攔截的泛型參數,它才工做.(這相似如class PersonCreatedEvent extends EntityCreatedEvent<Person>{...});
在特定的環境下,若是有全部的事件都遵循相同的結構(它多是上面提到的那種事件)會變得至關乏味.這樣的話,你可用實現ResolvableTypeProvider來指導運行時環境提供的架構.
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider { public EntityCreatedEvent(T entity) { super(entity); } @Override public ResolvableType getResolvableType() { return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource())); } }
它不只只對ApplicationEvent事件其做用,還適用於你做爲事件發佈的包含着的任意對象.
spring的默認事件是同步事件.
@EventListener @Transactional public void handleEvent(EventEvent event) { throw new NullPointerException("測試事務"); // System.out.println("同步步處理問題!!"); }
若是方法上加事務註解,則對其方法的事務其做用,如異常回滾.
若是方法上不加事務註解,則對其調用方法的事務無影響
若是方法加上事務註解,根據事務一致性和Transcational的默認事務傳播機制,則將該事務納入到上級方法的事務中,從而影響上級方法的事務.
開啓spring異步支持,才能使用異步事件.開啓方法:http://www.javashuo.com/article/p-qvvkxcsr-hd.html
經典代碼例子:
@EventListener @Async @Transactional public void handleEvent2(EventEvent event) throws Exception { Event event1 = event.getEvent(); event1.setName("異步事件事務一致性"); eventService.update(event1); System.out.println("異步輸出問題!!"); throw new NullPointerException("測試空指針"); }