guava学习(一):观察者模式
时间:2022-07-23
本文章向大家介绍guava学习(一):观察者模式,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
观察者模式是很常见的一种行为型设计模式。在Java原生态的实现方式中,观察者实现Observer接口,被观察者继承Observable。
下面编写一个使用Java api简单的实现。
观察者代码:
public class MyObserver implements Observer {
public void update(Observable o, Object arg) { if (o instanceof MyObservable){ System.out.println(arg); }
}}
被观察者:
public class MyObservable extends Observable {
@Override public void notifyObservers(Object message){ super.setChanged(); super.notifyObservers(message); }
}
绑定主题类:
public class Subject {
private Observable observable = new MyObservable();
public void registerObserver(MyObserver observer) { observable.addObserver(observer); }
public void removeObserver(MyObserver observer) { observable.deleteObserver(observer); }
public void notifyObservers(String message) { observable.notifyObservers(message); }}
测试代码
public static void main(String[] args) { Subject subject = new Subject(); MyObserver observer = new MyObserver(); subject.registerObserver(observer); subject.notifyObservers("hi, I am subject Observable");}
java的实现方式,如果观察者使用异步来实现消息处理,会使业务代码和非业务代码耦合在一起。
guava封装了Java的观察者模式,并且方便的支持异步。talk is cheap,先看一下代码:
定义2个观察者:
public class AObserver {
Logger logger = LoggerFactory.getLogger(getClass());
@Subscribe public void handleMessage(String msg){ logger.info("a obsesrver receive message:{}", msg); }}
public class BObserver {
Logger logger = LoggerFactory.getLogger(getClass());
@Subscribe public void handleMessage(String msg){ logger.info("b obsesrver receive message:{}", msg); }}
EventBusUtil类
public class EventBusUtil {
public static EventBus getEventBus(){ return EventBusFactory.getAsyncInstance(); }
public static class EventBusFactory{ private static EventBus asyncEventBus = new AsyncEventBus(LocalThreadPoolExecutor.getExecutor()); private static EventBus syncEventBus = new AsyncEventBus(MoreExecutors.directExecutor());
public static EventBus getAsyncInstance(){ return asyncEventBus; }
public static EventBus getyncInstance(){ return syncEventBus; }
}}
注意:MoreExecutors.directExecutor()看起来是线程池,其实是单线程,看源码注解:
测试代码:
public static void main(String[] args){ EventBus eventBus = EventBusUtil.getEventBus(); eventBus.register(new AObserver()); eventBus.register(new BObserver());
for (int j = 0; j < 2; j ++){ eventBus.post("hi, observer" + j); } }
下面看一下guava中的实现:
1)EventBus中的注册,可以注册任意对象作为观察者
/** * Registers all subscriber methods on {@code object} to receive events. * * @param object object whose subscriber methods should be registered. */ public void register(Object object) { subscribers.register(object); }
所有的观察者类中,处理监听事件的方法加了注解@Subscribe,注册的时候,会查找类中加了这个注解的方法然后进行注册,见下面代码中的
findAllSubscribers方法
/** Registers all subscriber methods on the given listener object. */ void register(Object listener) { //获取所有加了@Subscribe注解的方法 Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);
for (Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) { Class<?> eventType = entry.getKey(); Collection<Subscriber> eventMethodsInListener = entry.getValue();
CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
if (eventSubscribers == null) {//还没有注册 CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>(); eventSubscribers = MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet); }
eventSubscribers.addAll(eventMethodsInListener); } }
2)EventBus中的通知
/** * Posts an event to all registered subscribers. This method will return successfully after the * event has been posted to all subscribers, and regardless of any exceptions thrown by * subscribers. * * <p>If no subscribers have been subscribed for {@code event}'s class, and {@code event} is not * already a {@link DeadEvent}, it will be wrapped in a DeadEvent and reposted. * * @param event event to post. */ public void post(Object event) { Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event); if (eventSubscribers.hasNext()) { dispatcher.dispatch(event, eventSubscribers); } else if (!(event instanceof DeadEvent)) { // the event had no subscribers and was not itself a DeadEvent post(new DeadEvent(this, event)); } }
从上面代码可以看出,通过dispatcher.dispatch方法进行通知,这个方法的代码看下面代码:
/** Global event queue. */ private final ConcurrentLinkedQueue<EventWithSubscriber> queue = Queues.newConcurrentLinkedQueue();
@Override void dispatch(Object event, Iterator<Subscriber> subscribers) { checkNotNull(event); while (subscribers.hasNext()) { queue.add(new EventWithSubscriber(event, subscribers.next())); }
EventWithSubscriber e; while ((e = queue.poll()) != null) { e.subscriber.dispatchEvent(e.event); } }
上面的代码能够看出,消息事件event和观察者subscriber封装成一个对象放入并发队列中,然后出队让观察者触发消息处理。
/** Dispatches {@code event} to this subscriber using the proper executor. */ final void dispatchEvent(final Object event) { executor.execute( new Runnable() { @Override public void run() { try { invokeSubscriberMethod(event); } catch (InvocationTargetException e) { bus.handleSubscriberException(e.getCause(), context(event)); } } }); }
这儿的线程池正是我们在声明EventBus时传入的线程池变量。最后的事件触发使用了java的反射。
@VisibleForTesting void invokeSubscriberMethod(Object event) throws InvocationTargetException { try { method.invoke(target, checkNotNull(event)); } catch (IllegalArgumentException e) { throw new Error("Method rejected target/argument: " + event, e); } catch (IllegalAccessException e) { throw new Error("Method became inaccessible: " + event, e); } catch (InvocationTargetException e) { if (e.getCause() instanceof Error) { throw (Error) e.getCause(); } throw e; } }
代码分析就到这儿,guava详细代码请看这里:
https://github.com/google/guava
文中的示例代码请看这里
https://github.com/jinjunzhu/myguava.git
- hdu----(1849)Rabbit and Grass(简单的尼姆博弈)
- 10分钟让你明白MySQL是如何利用索引的
- 扩展Yarn资源模型详解1
- hdu-------(1848)Fibonacci again and again(sg函数版的尼姆博弈)
- go语言实现将重要数据写入图片中
- poj-----(2528)Mayor's posters(线段树区间更新及区间统计+离散化)
- poj---(2886)Who Gets the Most Candies?(线段树+数论)
- Hadoop3.0通用版集群安装高可靠详细教程
- Hadoop3.0集群安装知识
- 分布式消息队列 RocketMQ 源码分析 —— Message 顺序发送与消费
- 深入解析快速排序算法的原理及其Go语言版实现
- Hadoop3.0: YARN Resource配置说明
- GO语言利用K近邻算法实现小说鉴黄
- Why Spring Boot
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- redis源码之hash结构的实现
- redis源码之set结构
- redis源码之zset结构的实现
- Elasticsearch: range 数据类型及基于range的聚合 (7.4发行版新功能)
- 漫画:如何在数组中找到和为 “特定值” 的三个数?
- 除了MySQL,大牛DBA还会啥?
- 用 Docker swarm 快速部署分布式图数据库 Nebula Graph 集群
- Labelhub 基于腾讯云 Serverless 技术为人工智能企业提供数据与模型解决方案
- 手把手教你使用 Prometheus 监控 JVM
- 基于云开发 CloudBase 搭建在线视频会议应用
- 手搓一个分布式大气监测系统(六)云端能力更新、说明及源码放出
- maybe incorrect parameters such as bit_rate, rate, width or height
- vue 怎么将Checkbox 多选框选中的值提交
- vue-element怎么给select下拉框赋值?
- 小程序生成二维码海报的组件-wxa-plugin-canvas