應用場景:定義對象間的一種一對多的依賴關系,當一個對象的狀态發生改變時,所有依賴于它的對象
都得到通知并被自動更新。如在輿情系統中發現有客戶給出差評,就需要2小時内給相應的負責人發送短信通知,在客流系統中,人數超過預警值需要發送短信提醒相關負責人并記錄到預警流水表中。
特點:一般由兩個角色組成:釋出者和訂閱者(觀察者)。觀察者通常有一個回調,也可以沒有。例如可以适用于監聽器、日志收集、短信通知、郵件通知等場景。
在Java中通過Observable類和Observer接口實作了觀察者模式。一個Observer對象監視着一個Observable對象的變化,
當Observable對象發生變化時,Observer得到通知,就可以進行相應的工作。
java.util.Observable中有兩個方法對Observer特别重要,一個是setChange()方法用來設定一個内部标志位注明資料發
生了變化;一個是notifyObservers()方法會去調用一個清單中所有的Observer的update()方法,通知它們資料發生了變化。
Observer通過Observable的addObserver()方法把自己添加到這個清單中。這個清單雖然由Observable擁有,但
Observable并不知道到底有哪些Observer正在觀察等待通知。Observable隻提供一個方法讓Observer能把自己添加進清單,
并保證會去通知Observer發生了變化。通過這種機制,可以有任意多個Observer對Observable進行觀察,而不影響
Observable的實作。
1、預警類型
public enum WarnTypeEnum {
COMMENT,
KELIU;
}
2、訂閱觀察者
@Component
public class SubscriberObserver implements Observer{
private Map<String, Object> param;
private AScenic scenic;
private Integer count;
private WarnTypeEnum type;
@Autowired
private IZNoticeWarnService noticeWarnService;
@Override
public void update(Observable o, Object arg) {
if(o instanceof NoticeWarnSubject){
noticeWarnService.onSaveNoticeWarn(param,scenic,count,type);
}
if(o instanceof SMSWarnSubject){
noticeWarnService.onSendSMSWarn(param,scenic,count,type);
}
public WarnTypeEnum getType() {
return type;
}
public void setType(WarnTypeEnum type) {
this.type = type;
public Map<String, Object> getParam() {
return param;
public void setParam(Map<String, Object> param) {
this.param = param;
public AScenic getScenic() {
return scenic;
public void setScenic(AScenic scenic) {
this.scenic = scenic;
public Integer getCount() {
return count;
public void setCount(Integer count) {
this.count = count;
public SubscriberObserver() {
super();
3、主題對象
public class SMSWarnSubject extends Observable{
public void update(){
this.setChanged();
this.notifyObservers();
public class NoticeWarnSubject extends Observable{
4、業務實作,進行短信發送操作(這裡隻是僞代碼,不能直接運作),也要和增加的業務對相應的處理
public void onSendSMSWarn(Map<String, Object> c, AScenic sc, int count, WarnTypeEnum type) {
try {
if(type.equals(WarnTypeEnum.COMMENT)){
if (null!=c&&!StringUtils.isEmpty(c.get("phone").toString())) {
String[] param={c.get("name").toString(),c.get("num").toString()};
new SmsUtils(c.get("phone").toString(), properties.getSmsCommId(), param).start();
}
}else if(type.equals(WarnTypeEnum.KELIU)){
if (null!=sc&&!StringUtils.isEmpty(sc.getPhone())) {
String[] param={sc.getName(),count+""};
new SmsUtils(sc.getPhone(), properties.getSmsPersonId(), param).start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
5、在定時器中使用,因為我這裡使用了一個int類型的count這個參數,是以不能為空,預設可以加個0,例如observer.setCount(0);否則會就報空指針異常
private SubscriberObserver observer;
@Autowired
private NoticeWarnSubject noticeWarnSubject;
private SMSWarnSubject smsWarnSubject;
//景區輿情預警,觀察者模式
@Scheduled(cron="0 */20 * * * ?")
public void scenicCommentWarn() throws NoSuchMethodException, SecurityException {
log.info("輿情預警V2 start......");
List<SubscriberObserver> warnInfo = getCommentInformObserver();
addObservers(noticeWarnSubject, warnInfo); //儲存到未讀通知中
addObservers(smsWarnSubject, warnInfo);//發送輿情預警短信通知
noticeWarnSubject.update();
smsWarnSubject.update();
deleteObservers(noticeWarnSubject, warnInfo);
deleteObservers(smsWarnSubject, warnInfo);
log.info("輿情預警V2 end......");
//客流預警
@Scheduled(cron="0 */20 * * * ?")//20分鐘一次客流預警
public void scenicPersonWarn() {
log.info("客流預警V2 start......");
List<SubscriberObserver> warnInfo = getKeliutInformObserver();
addObservers(smsWarnSubject, warnInfo);//發送客流預警短信通知
log.info("客流預警V2 end......");
private List<SubscriberObserver> getKeliutInformObserver() {
List<SubscriberObserver> obs = new ArrayList<SubscriberObserver>();
Calendar cal = Calendar.getInstance();
List<AScenic> list=mapper.selectListByType(1);
String endTime = DateUtil.format(cal.getTime(), "yyyy-MM-dd HH:mm:ss");
cal.add(Calendar.MINUTE, -20);
String beginTime = DateUtil.format(cal.getTime(), "yyyy-MM-dd HH:mm:ss");
for (AScenic sc : list) {
int count = passengerCountMapper.selectMaxCountBySecond(sc.getName(),beginTime,endTime);
if(count>=sc.getWarnNum()){
//觸發預警
observer.setCount(count);
observer.setScenic(sc);
observer.setType(WarnTypeEnum.KELIU);
obs.add(observer);
}
}
return obs;
private List<SubscriberObserver> getCommentInformObserver() {
List<SubscriberObserver> obs = new ArrayList<SubscriberObserver>();
Calendar cal = Calendar.getInstance();
String endTime = DateUtil.format(cal.getTime(), "yyyy-MM-dd HH:mm:ss");
cal.add(Calendar.HOUR, -2);
String beginTime = DateUtil.format(cal.getTime(), "yyyy-MM-dd HH:mm:ss");
//查詢這兩個小時新産生的差評條數
List<Map<String,Object>> selectCommentListTask = commentMapper.selectCommentListWarnTask(null,null,null, null, beginTime, endTime, 0, 2, null);
for(Map<String,Object> c:selectCommentListTask){
observer.setParam(c);
observer.setCount(0);
observer.setType(WarnTypeEnum.COMMENT);
obs.add(observer);
}
return obs;
/**
* 将指定的觀察者清單添加到指定的主題
*
* @param subject
* @param list
*/
private void addObservers(Observable subject, List<SubscriberObserver> list) {
for (SubscriberObserver obs : list) {
subject.addObserver(obs);
private void deleteObservers(Observable subject,
List<SubscriberObserver> list) {
subject.deleteObserver(obs);
以上就是觀察者模式來實作短信通知和預警日志記錄的操作步驟了。
---------------------
作者:朱培
來源:CSDN
原文:https://blog.csdn.net/sdksdk0/article/details/85061646
版權聲明:本文為部落客原創文章,轉載請附上博文連結!