一、ApplicationListener
ApplicationContext提供事件處理通過ApplicationEvent類和ApplicationListener接口。如果一個bean實現ApplicationListener接口在容器中,每次一個ApplicationEvent被發布到ApplicationContext中,這類bean就會收到這些通知。
實現Spring事件機制主要有4個類:
ApplicationEvent:事件,每個實現類表示一類事件,可攜帶數據。
ApplicationListener:事件監聽器,用于接收事件處理時間。
ApplicationEventMulticaster:事件管理者,用于事件監聽器的注冊和事件的廣播。
ApplicationEventPublisher:事件發布者,委托ApplicationEventMulticaster完成事件發布。
- 事件就是一個包含了任意對象并含有事件對象創建時間戳的類。
框架源碼:
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;
}
}
- ApplicationListener
框架源碼:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}
當事件監聽器接收到它可以處理的事件,會調用onApplicationEvent()方法。注意到ApplicationListener是泛型參數的這樣可以參數化的定制事件。這意味著onApplicationEvent()方法可以保持類型安全,避免任何需要向下類型轉換。你可以盡可能多的注冊你希望事件偵聽器,但是注意,默認情況下,事件監聽器同步接收事件。這意味著publishEvent()方法會阻塞直到所有的事件監聽器成處理完事件。這種單線程同步方法的一個特點是,當一個監聽器接收到一個事件時,它運行在事務上下文的發布者線程上如果事務上下文可用。如果事件的發布需要另一種策略(譬如多線程)需要實現自己的 ApplicationEventMulticaster接口類。
- 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事件機制我們來解耦,將與注冊無關的操作改為異步。這里直接使用注解式寫法。
- 創建訂單實體
@Data
@Component
public class Order {
private String orderNo;
private String phone;
}
- 創建事件
/**
* 創建訂單完成事件
*/
@Component
public class AfterCreateOrderEvent extends ApplicationEvent {
public AfterCreateOrderEvent(Order order) {
super(order);
}
public Order getOrder(){
return (Order) getSource();
}
}
- 創建業務類進行事件發布與監聽
@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());
}
}
- 測試
@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);
}
}