应用场景:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象
都得到通知并被自动更新。如在舆情系统中发现有客户给出差评,就需要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、主题对象
@Component
public class SMSWarnSubject extends Observable{
public void update(){
this.setChanged();
this.notifyObservers();
}
}
@Component
public class NoticeWarnSubject extends Observable{
public void update(){
this.setChanged();
this.notifyObservers();
}
}
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);否则会就报空指针异常
@Autowired
private SubscriberObserver observer;
@Autowired
private NoticeWarnSubject noticeWarnSubject;
@Autowired
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(noticeWarnSubject, warnInfo); //保存到未读通知中
addObservers(smsWarnSubject, warnInfo);//发送客流预警短信通知
noticeWarnSubject.update();
smsWarnSubject.update();
deleteObservers(noticeWarnSubject, warnInfo);
deleteObservers(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) {
for (SubscriberObserver obs : list) {
subject.deleteObserver(obs);
}
}
以上就是观察者模式来实现短信通知和预警日志记录的操作步骤了。
---------------------
作者:朱培
来源:CSDN
原文:https://blog.csdn.net/sdksdk0/article/details/85061646
版权声明:本文为博主原创文章,转载请附上博文链接!