spring 事件

目前的版本是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就會執行其餘操做,且異步事件不會對事件源方法產生有效影響.這裏能夠認爲事件已經結束了.

EventListener詳解

咱們能夠經過註解的condition屬性來添加額外的運行時攔截條件,該屬性定義了一個SpEL表達式,你須要匹配這個特定事件,才能調用相應的方法.

例如,咱們的通知器能夠重寫爲只有當事件的test屬性等於foo時纔會調用.

@EventListener(condition = "#event.test == 'foo'")
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

每一次SpEL表達式評估都有專門的上下文.下面的表格展現了你在該上下文中能得到的選項,全部你可使用他們進行有條件的事件處理:

Event Spel available metadata

輸入圖片說明 名稱: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事件.若是你須要發佈幾個事件,只須要返回這些事件的集合.

異步事件處理方法不支持該特性.

Asynchronous Listeners 異步監聽器

若是你想要一個特別的監聽器來異步處理事件,簡單的使用常規的@Async支持:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

記住使用異步事件時注意如下幾個侷限:

  • 1.若是監聽器拋出一個異常,它不會傳播到調用者那裏.查看AsyncUncaughtExceptionHandler以應對更多細節.
  • 2.這些事件監聽器沒法發送回覆.若是你須要發送其餘事件做爲處理結果,注入ApplicationEventPublisher進行手動發送.

Ordering Listeners 監聽器排序

若是你想要一個監聽器先於其餘調用.只須要在方法參數上添加@Order註解;

@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

Generic Events 泛型事件

你可使用泛型更深刻的定義你的事件的結構.思考一個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("測試空指針");
    }
  • 自己事務有效.多是spring版本問題,有人說事務無效,但是在方法體內該事務仍是有效的.
  • 對於事件源所在的方法及其上級來講,該方法拋出的異常對他們無任何影響.
  • 若是該事件處理方法的業務邏輯容錯率低,則不建議使用異步事件.
相關文章
相關標籤/搜索