java使用观察者模式异步短信/邮箱提醒用户群
需求
用户中有人设置了账户余额达到阈值时,短信/邮箱进行提醒的服务。我们将需要在他账户余额阈值达到指定数值的时候进行短信/邮箱消息通知,允许账户余额阈值出现偏差的时候通知,如果某个用户48小时内已经短信/邮箱进行过通知了,那么将不再进行通知。
剖析
- 存在两个主题:短信通知和邮箱通知
- 存在两种观察者:设置了短信通知且账户余额到达阈值的用户,设置了邮箱通知且账户余额到达阈值的用户。
- 用spring的定时器,每10分钟去数据库获取某个主题已经达到阈值且开始了该主题的提醒功能的用户
- 用spring的@Asycn注解异步短信通知,邮箱通知的相关方法
- 用redis设置用户短信/邮箱为键名,设置过期时间为48小时。如果获取不到该键值对,说明其在观察者行列
代码
观察者父类
/** * 订阅观察者 * @author Administrator * */ @Component //标志为多例 @Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class SubscriberObserver implements Observer{ private String email; private String phone; private String username; @Autowired UserFunctionService UserFunctionService; @Override public void update(Observable o, Object arg) { if(o instanceof EmailAlertSubject){ UserFunctionService.alertUserEmail(email,username); } if(o instanceof PhoneAlertSubject){ UserFunctionService.alertUserPhone(phone,username); } } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public SubscriberObserver() { super(); // TODO Auto-generated constructor stub } }
主题
/** * email提醒主题 * @author Administrator * */ @Component public class EmailAlertSubject extends Observable{ public void alert(){ this.setChanged(); //如果用拉的方式,这么调用 this.notifyObservers(); } }
/** * 短信提醒主题 * @author Administrator * */ @Component public class PhoneAlertSubject extends Observable{ public void alert(){ this.setChanged(); //如果用拉的方式,这么调用 this.notifyObservers(); } }
定时器
/** * 定时给订阅了短信提醒和email提醒的用户服务 * @author Administrator * */ @Component public class TimeAlertTaskUtil { @Autowired CommonUserService commonUserService; @Autowired JedisConnectionFactory factory; @Autowired EmailAlertSubject emailSubject; @Autowired PhoneAlertSubject phoneSubject; private static final String emailKeyName = "emailAlert:"; private static final String phoneKeyName = "phoneAlert:"; /** * 定时获取需要email提醒的用户,每10分钟调用一次 */ @Scheduled(fixedDelay = 1000 * 60 * 10) public void alertEmailTask() { // 1.获取数据库中达到了阈值的用户 List<User> emails = commonUserService.getUserAlertEmailAndName(); // 2.查看redis中是否有达到阈值,且48小时已经通知的用户,将其排除在观察者行列,最终得出观察者队伍 List<SubscriberObserver> informEmail = getInformObserver(emails); // 3.创建主题,添加观察者 addObservers(emailSubject, informEmail); // 4.通知 emailSubject.alert(); // 5.将已经通知的观察者信息存储到reids内,设置过期时间为一天 setRedisCache(emails); // 6.将观察者从主题中移除 deleteObservers(emailSubject, informEmail); } /** * 定时获取需要短信提醒的用户,每10分钟调用一次 * */ @Scheduled(fixedDelay = 1000 * 60 * 10) public void alertPhoneTask() { // 1.获取数据库中达到了阈值的用户 List<User> phones = commonUserService.getUserAlertPhoneAndName(); // 2.查看redis中是否有达到阈值,且今天已经通知的用户,将其排除在观察者行列,最终得出观察者队伍 List<SubscriberObserver> informPhones = getInformObserver(phones); // 3.创建主题,添加观察者 addObservers(phoneSubject, informPhones); // 4.通知 phoneSubject.alert(); // 5.将已经通知的观察者信息存储到reids内,设置过期时间为一天 setRedisCache(phones); // 6.将观察者从主题中移除 deleteObservers(phoneSubject, informPhones); } /** * ------------------------------------------------------------------------ * ----------------------------------------------------- **/ /** * 过滤掉今天已经email提醒的用户,返回真正需要提醒的观察者列表 * * @param emails * @return */ private List<SubscriberObserver> getInformObserver( List<User> users) { List<SubscriberObserver> obs = new ArrayList<SubscriberObserver>(); Jedis jedis = factory.getConnection().getNativeConnection(); for (User user : users) { String value; SubscriberObserver observer = (SubscriberObserver) SpringConfigTool .getBean("subscriberObserver"); if (user.getEmail()!=null) { value = jedis.get(emailKeyName + user.getEmail()); if (value == null || !value.equals("success")) { observer.setEmail(user.getEmail()); observer.setUsername(user.getName()); obs.add(observer); } } else { value = jedis.get(phoneKeyName + user.getPhone()); if (value == null || !value.equals("success")) { observer.setPhone(user.getPhone()); observer.setUsername(user.getName()); 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); } } /** * 将列表的值作为键,存入redis,过期时间为48小时 * * @param list */ private void setRedisCache(List<User> users) { Jedis jedis = factory.getConnection().getNativeConnection(); for (User user : users) { if (user.getEmail()!=null) { jedis.set(emailKeyName + user.getEmail(), "success", "NX", "EX", 60 * 60 * 24 * 2); } else { jedis.set(phoneKeyName + user.getPhone(), "success", "NX", "EX", 60 * 60 * 24 * 2); } } } }
总结
代码是不全面的,只是个示例而已。关于短信通知和邮箱通知的服务类和工具类并没有给出,因为里面涉及到一些隐私参数。所以关于异步通知示例代码没有,但使用Spring管理的@Async注解和在spring进行一定的配置即可,可以在我的另外一篇博客找到关于异步通知的示例代码。
事实上根据需求,可以使用redis的发布订阅,或者消息队列mq来实现类似的功能。但为了加深对设计模式的理解,所以写了一个不是很纯正的观察者模式来模仿发布订阅的操作。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
赞 (0)