SpringBoot--開發實戰--監聽器(四十一)

一、ApplicationListener

ApplicationContext提供事件處理通過ApplicationEvent類和ApplicationListener接口。如果一個bean實現ApplicationListener接口在容器中,每次一個ApplicationEvent被發布到ApplicationContext中,這類bean就會收到這些通知。
實現Spring事件機制主要有4個類:
ApplicationEvent:事件,每個實現類表示一類事件,可攜帶數據。
ApplicationListener:事件監聽器,用于接收事件處理時間。
ApplicationEventMulticaster:事件管理者,用于事件監聽器的注冊和事件的廣播。
ApplicationEventPublisher:事件發布者,委托ApplicationEventMulticaster完成事件發布。

  1. 事件就是一個包含了任意對象并含有事件對象創建時間戳的類。
    框架源碼:
public abstract class ApplicationEvent extends EventObject {
    private static final long serialVersionUID = 7099057708183571937L;
    private final long timestamp = System.currentTimeMillis();

    public ApplicationEvent(Object source) {
        super(source);
    }

    public final long getTimestamp() {
        return this.timestamp;
    }
}
  1. ApplicationListener
    框架源碼:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}

當事件監聽器接收到它可以處理的事件,會調用onApplicationEvent()方法。注意到ApplicationListener是泛型參數的這樣可以參數化的定制事件。這意味著onApplicationEvent()方法可以保持類型安全,避免任何需要向下類型轉換。你可以盡可能多的注冊你希望事件偵聽器,但是注意,默認情況下,事件監聽器同步接收事件。這意味著publishEvent()方法會阻塞直到所有的事件監聽器成處理完事件。這種單線程同步方法的一個特點是,當一個監聽器接收到一個事件時,它運行在事務上下文的發布者線程上如果事務上下文可用。如果事件的發布需要另一種策略(譬如多線程)需要實現自己的 ApplicationEventMulticaster接口類。

  1. ApplicationEventMulticaster
    ApplicationEventMulticaster接口方法分為三類,注冊事件監聽器、移除事件監聽器、發布事件。

二、Spring內置事件

事件 描述
ContextRefreshedEvent 事件發布在ApplicationContext初始化或刷新時(例如,通過在ConfigurableApplicationContext接口使用refresh()方法)。這里,“初始化”意味著所有bean加載,post-processor bean被檢測到并且激活,單例預先實例化,ApplicationContext對象可以使用了。只要上下文沒有關閉,可以觸發多次刷新,ApplicationContext提供了一種可選擇的支持這種“熱”刷新。例如,XmlWebApplicationContext支持熱刷新,但GenericApplicationContext并非如此。具體是在AbstractApplicationContext的finishRefresh()方法中。
ContextStartedEvent 事件發布在ApplicationContext開始使用ConfigurableApplicationContext接口start()方法。這里,“開始”意味著所有生命周期bean接收到一個明確的起始信號。通常,這個信號用于明確停止后重新啟動,但它也可以用于啟動組件沒有被配置為自動運行(例如,組件還沒有開始初始化)。
ContextStoppedEvent 事件發布在ApplicationContext停止時通過使用ConfigurableApplicationContext接口上的stop()方法。在這里,“停止”意味著所有生命周期bean接收一個顯式的停止信號。停止上下文可以通過重新調用start()方法。
ContextClosedEvent 事件發布在ApplicationContext關閉時通過關閉ConfigurableApplicationContext接口()方法。這里,“封閉”意味著所有單例bean被摧毀。一個封閉的環境達到生命的終結。它不能刷新或重啟。
RequestHandledEvent 一個特定的web事件告訴所有能處理HTTP請求的bean 。這個事件是在請求完成后發布的。這個事件只適用于使用Spring的DispatcherServlet的web應用程序。

三、示例

當一個用戶完成貸款訂單后,我們希望執行發送提醒短信、調用積分服務增加積分、通知風控服務重算風控值(后續操作可能增加)等功能。

//創建訂單
public void createOrder(Order order){
創建貸款訂單;
發送提醒短信;
調用積分服務增加積分;
調用風控服務推送訂單信息;
……
返回;
}

隨著業務復雜度的增加,我們很快發現createOrder()創建訂單這個方法耦合了太多與注冊無關的邏輯,即影響了原本創建訂單方法的效率,在設計上又不符合“開閉原則”。
現在使用spring事件機制我們來解耦,將與注冊無關的操作改為異步。這里直接使用注解式寫法。

  1. 創建訂單實體
@Data
@Component
public class Order {
    private String orderNo;
    private String phone;
}
  1. 創建事件
/**
 * 創建訂單完成事件
 */
@Component
public class AfterCreateOrderEvent extends ApplicationEvent {
    public AfterCreateOrderEvent(Order order) {
        super(order);
    }
    public Order getOrder(){
        return (Order) getSource();
    }
}
  1. 創建業務類進行事件發布與監聽
@Service
public class OrderService {
    //直接注入spring事件發布者
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 簡單的創建訂單方法
     */
    public void createOrder(Order order) {
        System.out.println("創建訂單 order:" + order.getOrderNo() + " 結束");
        // TODO 1. 調用事件發布者發布事件,解耦
        applicationEventPublisher.publishEvent(new AfterCreateOrderEvent(order));
        System.out.println("createOrder方法 結束");
    }

    // TODO 2. 事件監聽處理,加入@EventListener注解后,該方法可以看出一個事件監聽者
    @EventListener
    public void afterCreateOrder(AfterCreateOrderEvent afterCreateOrderEvent) throws InterruptedException {
        Order order = afterCreateOrderEvent.getOrder();
        Thread.sleep(2000);
        System.out.println("調用短信通知服務:" + order.getPhone());
        System.out.println("調用積分服務增加貸款積分:" + order.getOrderNo());
    }
}
  1. 測試
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringSessionApplicationTests {
    @Autowired
    private OrderService orderService;

    @Test
    public void eventTest() {
        Order order = new Order();
        order.setOrderNo("N123124124124");
        order.setPhone("18782202534");
        orderService.createOrder(order);
    }
}
結果
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容