談到Spring 事件驅動模型,我想你們都不陌生,事件驅動模型,一般也能夠說是觀察者設計模式,對觀察者設計模式不熟悉的朋友能夠看我以前寫的筆記,設計模式java語言實現之觀察者模式,在java事件驅動的支持中,EventBus作移動端開發的朋友應該都比較瞭解,其實,java自己也自帶了對事件驅動的支持,可是大部分都是用於咱們的客戶端開發,好比GUI ,Swing這些,而Spring 則在java的基礎上,擴展了對事件驅動的支持。java
不說廢話,直接上代碼git
首先,咱們新建一個類NotifyEvent 繼承ApplicationEvent,用於封裝咱們事件額外的信息,這裏則是String類型的msg,用於記錄詳細的事件內容。github
public class NotifyEvent extends ApplicationEvent {
private String msg;
public NotifyEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
public String getMsg() {
return msg;
}
}
複製代碼
其中,ApplicationEvent 是一個抽象類,擴展了java自己的EventObject 類,每個繼承了ApplicationEvent的子類都表示一類事件,能夠攜帶數據。web
而後新建一個NotifyPublisher用於咱們事件的發佈工做,該類實現了ApplicationContextAware並重寫了setApplicationContext 方法,這一步的目的是能夠獲取咱們Spring的應用上下文,由於事件的發佈是須要應用上下文來作的,不瞭解應用上下文的同窗能夠去看個人另一篇筆記:到底什麼是上下文?編程
@Component //聲明成組件,爲了後期注入方便
public class NotifyPublisher implements ApplicationContextAware {
private ApplicationContext ctx; //應用上下文
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx= applicationContext;
}
// 發佈一個消息,這裏你們能夠根據不一樣的狀態實現發佈不一樣的事件,我這裏就只寫了一個事件類,因此if else
//都發布NotifyEvent事件。
public void publishEvent(int status, String msg) {
if (status == 0) {
ctx.publishEvent(new NotifyEvent(this, msg));
} else {
ctx.publishEvent(new NotifyEvent(this,msg)) ;
}
}
}
複製代碼
最後一步就是實現一個類做爲事件的訂閱者啦,當事件發佈時,會通知訂閱者,而後訂閱者作相關的處理,好比新用戶註冊發送事件自動發送歡迎郵件等等。同時,Spring 4.2 版本更新的EventListener,能夠很方便幫助咱們實現事件與方法的綁定,只須要在目標方法上加上EventListener便可。設計模式
@Component
public class NotifyListener {
@EventListener
//參數NotifyEvent ,當有NotifyEvent 類型的事件發生時,交給sayHello方法處理
public void sayHello(NotifyEvent notifyEvent){
System.out.println("收到事件:"+notifyEvent.getMsg());
}
}
複製代碼
**測試:**編寫咱們的測試類TestController。瀏覽器
@RestController
public class TestController {
@Autowired
private NotifyPublisher notifyPublisher;
@GetMapping("/sayHello")
public String sayHello(){
notifyPublisher.publishEvent(1, "我發佈了一個事件");
return "Hello Word";
}
}
複製代碼
啓動咱們的應用,在瀏覽器中輸入http://127.0.0.1:8080/sayHello,控制檯輸出:app
2019-09-28 16:55:51.902 INFO 716 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms
收到事件:我發佈了一個事件
複製代碼
劃個知識點:異步
若是一個新的事件繼承了NotifyEvent,當咱們推送NotifyEvent類型的事件時,NotifyEvent和其子類的監聽器均可以收到該事件。ide
完了嗎,尚未,平常除了聽到過事件驅動編程,偶爾還會見到異步事件驅動編程這幾個字,一樣的Spring 也提供了@Async 註解來實現異步事件的消費。用起來也很簡單,只須要在 @EventListener上加上@Async 就行了。
代碼以下:
@Component
public class NotifyListener {
@Async
@EventListener
public void sayHello(NotifyEvent notifyEvent){
System.out.println("收到事件:"+notifyEvent.getMsg());
}
}
複製代碼
最後配置一個線程池
@Configuration
@EnableAsync
public class AysncListenerConfig implements AsyncConfigurer {
/** * 獲取異步線程池執行對象 * * @return */
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
executor.setCorePoolSize(10); //核心線程數
executor.setMaxPoolSize(20); //最大線程數
executor.setQueueCapacity(1000); //隊列大小
executor.setKeepAliveSeconds(300); //線程最大空閒時間
executor.setThreadNamePrefix("ics-Executor-"); ////指定用於新建立的線程名稱的前綴。
executor.setRejectedExecutionHandler(
new ThreadPoolExecutor.CallerRunsPolicy()); // 拒絕策略
return new ExceptionHandlingAsyncTaskExecutor(executor);
}
}
複製代碼
public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor {
private AsyncTaskExecutor executor;
public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) {
this.executor = executor;
}
//用獨立的線程來包裝,@Async其本質就是如此
public void execute(Runnable task) {
executor.execute(createWrappedRunnable(task));
}
public void execute(Runnable task, long startTimeout) {
//用獨立的線程來包裝,@Async其本質就是如此
executor.execute(createWrappedRunnable(task), startTimeout);
}
public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task));
//用獨立的線程來包裝,@Async其本質就是如此。
}
public Future submit(final Callable task) {
//用獨立的線程來包裝,@Async其本質就是如此。
return executor.submit(createCallable(task));
}
private Callable createCallable(final Callable task) {
return new Callable(){
@Override
public Object call() throws Exception {
try {
return task.call();
} catch (Exception ex) {
handle(ex);
throw ex;
}
}
};
}
private Runnable createWrappedRunnable(final Runnable task) {
return new Runnable() {
public void run() {
try {
task.run();
} catch (Exception ex) {
handle(ex);
}
}
};
}
private void handle(Exception ex) {
//具體的異常邏輯處理的地方
System.err.println("Error during @Async execution: " + ex);
}
}
複製代碼
測試:編寫咱們的測試類TestController。
2019-09-28 16:55:51.902 INFO 716 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms
收到事件:我發佈了一個事件
複製代碼
大功告成啦。
電子版筆記和代碼已經開源至github(歡迎star哦):