觀察者模式實際應用場景-----Spring事件機制
阿新 • • 發佈:2019-01-10
以下虛擬碼是一個儲存訂單的功能,並會發送簡訊訊息:
/**
* Author heling on 2019/1/9
*/
@Service
public class OrderServiceImpl implements OrderService {
@Override
public void saveOrder() {
//1.建立訂單
System.out.println("訂單建立成功");
//2.傳送簡訊
System.out.println("恭喜您訂單建立成功!----by sms");
}
}
現有新需求:需要加一個微信通知的功能,程式碼如下:
/** * Author heling on 2019/1/9 */ @Service public class OrderServiceImpl implements OrderService { @Override public void saveOrder() { //1.建立訂單 System.out.println("訂單建立成功"); //2.傳送簡訊 System.out.println("恭喜您訂單建立成功!----by sms"); //新需求:微信通知 // 3.傳送微信 System.out.println("恭喜您訂單建立成功!----by wechat"); } }
存在問題:每次建立訂單需要加新功能(如新的通知方式),則要修改原有的類,難以維護。
違背設計模式的原則
1.單一職責:訂單儲存功能,雜糅了訊息通知這些功能
2.開閉原則:對拓展開放,對修改關閉
優化方案:使用觀察者模式,使建立訂單和訊息通知進行分離,低耦合。可以選擇訊息佇列,spring事件機制等,本文選擇Spring事件機制。
改造開始:
1.建立事件
package com.pengshu.magicwallet.admin.test; import org.springframework.context.ApplicationEvent; import java.util.List; /** * Author heling on 2019/1/9 * 訂單建立活動事件 */ public class OrderCreateEvent extends ApplicationEvent { private String name; //訊息引數 private List<String> contentList; public OrderCreateEvent(Object source, String name, List<String> contentList) { super(source); this.name = name; this.contentList = contentList; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<String> getContentList() { return contentList; } public void setContentList(List<String> contentList) { this.contentList = contentList; } }
2.監聽器
package com.pengshu.magicwallet.admin.test;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* Author heling on 2019/1/9
* 簡訊監聽器
* ApplicationListener是無序的
*/
@Component
public class SmsListener implements ApplicationListener<OrderCreateEvent> {
@Override
public void onApplicationEvent(OrderCreateEvent event) {
//傳送簡訊
System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by sms");
}
}
package com.pengshu.magicwallet.admin.test;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* Author heling on 2019/1/9
* 微信監聽器
*/
@Component
public class WechatListener implements ApplicationListener<OrderCreateEvent> {
@Override
public void onApplicationEvent(OrderCreateEvent event) {
//傳送微信
System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by wechat");
}
}
3.事件釋出
package com.pengshu.magicwallet.admin.test;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
/**
* Author heling on 2019/1/9
*/
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private ApplicationContext applicationContext;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void saveOrder() {
//1.建立訂單
System.out.println("訂單建立成功");
//2.釋出事件
ArrayList<String> contentList = new ArrayList<>();
contentList.add("heling");
contentList.add("123456789");
OrderCreateEvent orderCreateEvent = new OrderCreateEvent(this, "訂單建立", contentList);
applicationContext.publishEvent(orderCreateEvent);//ApplicationContext是我們的事件容器上層,我們釋出事件,也可以通過此容器完成釋出
//applicationEventPublisher.publishEvent(orderCreateEvent);//也可以
System.out.println("finished!");
}
}
列印結果:
訂單建立成功
heling,您的訂單:123456789建立成功! ----by sms
heling,您的訂單:123456789建立成功! ----by wechat
finished!
如何非同步執行監聽器?
1.springboot開啟事件非同步設定
package com.pengshu.magicwallet;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@MapperScan("com.pengshu.magicwallet.mapper")
@PropertySource("classpath:authority.properties")
@EnableTransactionManagement
@EnableAsync //開啟spring事件非同步設定,加@Async註解
public class MagicWalletAdminApplication {
public static void main(String[] args) {
SpringApplication.run(MagicWalletAdminApplication.class, args);
}
}
2.監聽器類或方法新增@Async註解
列印結果:
訂單建立成功
finished!
heling,您的訂單:123456789建立成功! ----by sms
heling,您的訂單:123456789建立成功! ----by wechat
如何制定監聽器執行順序?
package com.pengshu.magicwallet.admin.test;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;
/**
* Author heling on 2019/1/9
* 微信監聽器
* SmartApplicationListener可以設定順序等
*/
@Component
public class WechatListener implements SmartApplicationListener {
//設定監聽優先順序
@Override
public int getOrder() {
return 1;
}
//監聽器智慧所在之一,能夠根據事件型別動態監聽
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass == OrderCreateEvent.class;
}
//監聽器智慧所在之二,能夠根據事件釋出者型別動態監聽
@Override
public boolean supportsSourceType(Class<?> aClass) {
return aClass == OrderServiceImpl.class;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
OrderCreateEvent event = (OrderCreateEvent) applicationEvent;
//傳送微信
System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by wechat");
}
// @Override
// @Async
// public void onApplicationEvent(OrderCreateEvent event) {
//
// //傳送微信
// System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by wechat");
//
// }
}
package com.pengshu.magicwallet.admin.test;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;
/**
* Author heling on 2019/1/9
* 簡訊監聽器
*/
@Component
public class SmsListener implements SmartApplicationListener {
@Override
public int getOrder() {
return 2;
}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass == OrderCreateEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> aClass) {
return aClass == OrderServiceImpl.class;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
OrderCreateEvent event = (OrderCreateEvent) applicationEvent;
//傳送簡訊
System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by sms");
}
// @Override
// @Async
// public void onApplicationEvent(OrderCreateEvent event) {
//
// //傳送簡訊
// System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by sms");
//
// }
}
列印結果:
訂單建立成功
heling,您的訂單:123456789建立成功! ----by wechat
heling,您的訂單:123456789建立成功! ----by sms
finished!
在實現了SmartApplicationListener的監聽器中,我們通過重寫GetOrder方法來修改不同監聽器的順序,優先順序越小,則越先被呼叫。通過配置不同的優先順序,且讓監聽器之間阻塞呼叫。我們就能實現流水線式的有序事件呼叫,這在實際應用場景中還是蠻有意義的