一咏连、ApplicationListener
ApplicationContext提供事件處理通過ApplicationEvent類和ApplicationListener接口。如果一個(gè)bean實(shí)現(xiàn)ApplicationListener接口在容器中,每次一個(gè)ApplicationEvent被發(fā)布到ApplicationContext中,這類bean就會(huì)收到這些通知币砂。
實(shí)現(xiàn)Spring事件機(jī)制主要有4個(gè)類:
ApplicationEvent:事件,每個(gè)實(shí)現(xiàn)類表示一類事件攒发,可攜帶數(shù)據(jù)有滑。
ApplicationListener:事件監(jiān)聽器,用于接收事件處理時(shí)間壹士。
ApplicationEventMulticaster:事件管理者磷雇,用于事件監(jiān)聽器的注冊(cè)和事件的廣播。
ApplicationEventPublisher:事件發(fā)布者躏救,委托ApplicationEventMulticaster完成事件發(fā)布唯笙。
- 事件就是一個(gè)包含了任意對(duì)象并含有事件對(duì)象創(chuàng)建時(shí)間戳的類。
框架源碼:
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);
}
當(dāng)事件監(jiān)聽器接收到它可以處理的事件盒使,會(huì)調(diào)用onApplicationEvent()方法崩掘。注意到ApplicationListener是泛型參數(shù)的這樣可以參數(shù)化的定制事件。這意味著onApplicationEvent()方法可以保持類型安全,避免任何需要向下類型轉(zhuǎn)換少办。你可以盡可能多的注冊(cè)你希望事件偵聽器,但是注意,默認(rèn)情況下,事件監(jiān)聽器同步接收事件苞慢。這意味著publishEvent()方法會(huì)阻塞直到所有的事件監(jiān)聽器成處理完事件。這種單線程同步方法的一個(gè)特點(diǎn)是,當(dāng)一個(gè)監(jiān)聽器接收到一個(gè)事件時(shí),它運(yùn)行在事務(wù)上下文的發(fā)布者線程上如果事務(wù)上下文可用英妓。如果事件的發(fā)布需要另一種策略(譬如多線程)需要實(shí)現(xiàn)自己的 ApplicationEventMulticaster接口類挽放。
- ApplicationEventMulticaster
ApplicationEventMulticaster接口方法分為三類,注冊(cè)事件監(jiān)聽器蔓纠、移除事件監(jiān)聽器辑畦、發(fā)布事件。
二腿倚、Spring內(nèi)置事件
事件 | 描述 |
---|---|
ContextRefreshedEvent | 事件發(fā)布在ApplicationContext初始化或刷新時(shí)(例如,通過在ConfigurableApplicationContext接口使用refresh()方法)纯出。這里,“初始化”意味著所有bean加載,post-processor bean被檢測(cè)到并且激活,單例預(yù)先實(shí)例化,ApplicationContext對(duì)象可以使用了。只要上下文沒有關(guān)閉,可以觸發(fā)多次刷新,ApplicationContext提供了一種可選擇的支持這種“熱”刷新猴誊。例如,XmlWebApplicationContext支持熱刷新,但GenericApplicationContext并非如此潦刃。具體是在AbstractApplicationContext的finishRefresh()方法中。 |
ContextStartedEvent | 事件發(fā)布在ApplicationContext開始使用ConfigurableApplicationContext接口start()方法懈叹。這里,“開始”意味著所有生命周期bean接收到一個(gè)明確的起始信號(hào)乖杠。通常,這個(gè)信號(hào)用于明確停止后重新啟動(dòng),但它也可以用于啟動(dòng)組件沒有被配置為自動(dòng)運(yùn)行(例如,組件還沒有開始初始化)。 |
ContextStoppedEvent | 事件發(fā)布在ApplicationContext停止時(shí)通過使用ConfigurableApplicationContext接口上的stop()方法澄成。在這里,“停止”意味著所有生命周期bean接收一個(gè)顯式的停止信號(hào)胧洒。停止上下文可以通過重新調(diào)用start()方法畏吓。 |
ContextClosedEvent | 事件發(fā)布在ApplicationContext關(guān)閉時(shí)通過關(guān)閉ConfigurableApplicationContext接口()方法。這里,“封閉”意味著所有單例bean被摧毀卫漫。一個(gè)封閉的環(huán)境達(dá)到生命的終結(jié)菲饼。它不能刷新或重啟。 |
RequestHandledEvent | 一個(gè)特定的web事件告訴所有能處理HTTP請(qǐng)求的bean 列赎。這個(gè)事件是在請(qǐng)求完成后發(fā)布的宏悦。這個(gè)事件只適用于使用Spring的DispatcherServlet的web應(yīng)用程序。 |
三包吝、示例
當(dāng)一個(gè)用戶完成貸款訂單后饼煞,我們希望執(zhí)行發(fā)送提醒短信、調(diào)用積分服務(wù)增加積分诗越、通知風(fēng)控服務(wù)重算風(fēng)控值(后續(xù)操作可能增加)等功能砖瞧。
//創(chuàng)建訂單
public void createOrder(Order order){
創(chuàng)建貸款訂單;
發(fā)送提醒短信嚷狞;
調(diào)用積分服務(wù)增加積分块促;
調(diào)用風(fēng)控服務(wù)推送訂單信息;
……
返回床未;
}
隨著業(yè)務(wù)復(fù)雜度的增加竭翠,我們很快發(fā)現(xiàn)createOrder()創(chuàng)建訂單這個(gè)方法耦合了太多與注冊(cè)無關(guān)的邏輯,即影響了原本創(chuàng)建訂單方法的效率即硼,在設(shè)計(jì)上又不符合“開閉原則”逃片。
現(xiàn)在使用spring事件機(jī)制我們來解耦屡拨,將與注冊(cè)無關(guān)的操作改為異步只酥。這里直接使用注解式寫法。
- 創(chuàng)建訂單實(shí)體
@Data
@Component
public class Order {
private String orderNo;
private String phone;
}
- 創(chuàng)建事件
/**
* 創(chuàng)建訂單完成事件
*/
@Component
public class AfterCreateOrderEvent extends ApplicationEvent {
public AfterCreateOrderEvent(Order order) {
super(order);
}
public Order getOrder(){
return (Order) getSource();
}
}
- 創(chuàng)建業(yè)務(wù)類進(jìn)行事件發(fā)布與監(jiān)聽
@Service
public class OrderService {
//直接注入spring事件發(fā)布者
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
/**
* 簡(jiǎn)單的創(chuàng)建訂單方法
*/
public void createOrder(Order order) {
System.out.println("創(chuàng)建訂單 order:" + order.getOrderNo() + " 結(jié)束");
// TODO 1. 調(diào)用事件發(fā)布者發(fā)布事件,解耦
applicationEventPublisher.publishEvent(new AfterCreateOrderEvent(order));
System.out.println("createOrder方法 結(jié)束");
}
// TODO 2. 事件監(jiān)聽處理呀狼,加入@EventListener注解后裂允,該方法可以看出一個(gè)事件監(jiān)聽者
@EventListener
public void afterCreateOrder(AfterCreateOrderEvent afterCreateOrderEvent) throws InterruptedException {
Order order = afterCreateOrderEvent.getOrder();
Thread.sleep(2000);
System.out.println("調(diào)用短信通知服務(wù):" + order.getPhone());
System.out.println("調(diào)用積分服務(wù)增加貸款積分:" + order.getOrderNo());
}
}
- 測(cè)試
@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);
}
}