java eventbus 原理_EventBus原理解析

前言

EventBus的核心思想是观察者模式 (生产/消费者编程模型) 。

通过前面的文章我们已经知道,如何使用eventBus了。我们需要先定义一个Observer(前文中的EventListener类),然后将其注册到eventBus里,通过 @Subscribe 定义消息回调函数。

那我们先看看register(Object object) 和unregister(Object object) 方法。

register (Object object) 解析

public void register(Object object) {

Multimap, EventSubscriber> methodsInListener =

finder.findAllSubscribers(object);

subscribersByTypeLock.writeLock().lock();

try {

subscribersByType.putAll(methodsInListener);

} finally {

subscribersByTypeLock.writeLock().unlock();

}

}

1

2

3

4

5

6

7

8

9

10

publicvoidregister(Objectobject){

Multimap,EventSubscriber>methodsInListener=

finder.findAllSubscribers(object);

subscribersByTypeLock.writeLock().lock();

try{

subscribersByType.putAll(methodsInListener);

}finally{

subscribersByTypeLock.writeLock().unlock();

}

}

可以看到是先通过SubscriberFindingStrategy接口里的findAllSubscribers方法获取所有标记了@ Subscribe 注解的方法,其中该接口的具体实现是AnnotatedSubscriberFinder类。放到一个guava里定义的Multimap里。然后是把获取到的methodsInListener放到一个叫subscribersByType的 guava里定义的SetMultimap里 。

public Multimap, EventSubscriber> findAllSubscribers(Object listener) {

Multimap, EventSubscriber> methodsInListener = HashMultimap.create();

Class> clazz = listener.getClass();

for (Method method : getAnnotatedMethods(clazz)) {

Class>[] parameterTypes = method.getParameterTypes();

Class> eventType = parameterTypes[0];

EventSubscriber subscriber = makeSubscriber(listener, method);

methodsInListener.put(eventType, subscriber);

}

return methodsInListener;

}

1

2

3

4

5

6

7

8

9

10

11

publicMultimap,EventSubscriber>findAllSubscribers(Objectlistener){

Multimap,EventSubscriber>methodsInListener=HashMultimap.create();

Class>clazz=listener.getClass();

for(Methodmethod:getAnnotatedMethods(clazz)){

Class>[]parameterTypes=method.getParameterTypes();

Class>eventType=parameterTypes[0];

EventSubscribersubscriber=makeSubscriber(listener,method);

methodsInListener.put(eventType,subscriber);

}

returnmethodsInListener;

}

findAllSubscribers方法里,最重要的是methodsInListener,它的结构可以简单理解为一个map,其中key是eventType,在我前文写的例子中就是com.sww.eventbus.domain.MessageEvent,其中value是subscriber,就是例子中的com.sww.eventbus.listener.EventListener#onMessageEvent。

总之,一句话就是先通过标记找到所有已经注册进来的观察者,然后存放到容器里备用。

那unregister就是从容器删除它们,

unRegister (Object object) 解析

public void unregister(Object object) {

Multimap, EventSubscriber> methodsInListener = finder.findAllSubscribers(object);

for (Entry, Collection> entry :

methodsInListener.asMap().entrySet()) {

Class> eventType = entry.getKey();

Collection eventMethodsInListener = entry.getValue();

subscribersByTypeLock.writeLock().lock();

try {

Set currentSubscribers = subscribersByType.get(eventType);

if (!currentSubscribers.containsAll(eventMethodsInListener)) {

throw new IllegalArgumentException(

"missing event subscriber for an annotated method. Is " + object + " registered?");

}

currentSubscribers.removeAll(eventMethodsInListener);

} finally {

subscribersByTypeLock.writeLock().unlock();

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

publicvoidunregister(Objectobject){

Multimap,EventSubscriber>methodsInListener=finder.findAllSubscribers(object);

for(Entry,Collection>entry:

methodsInListener.asMap().entrySet()){

Class>eventType=entry.getKey();

CollectioneventMethodsInListener=entry.getValue();

subscribersByTypeLock.writeLock().lock();

try{

SetcurrentSubscribers=subscribersByType.get(eventType);

if(!currentSubscribers.containsAll(eventMethodsInListener)){

thrownewIllegalArgumentException(

"missing event subscriber for an annotated method. Is "+object+" registered?");

}

currentSubscribers.removeAll(eventMethodsInListener);

}finally{

subscribersByTypeLock.writeLock().unlock();

}

}

}

post( Object event)解析

有了观察者,下面就是发送事件了,阅读过前文会知道是通过eventBus.post(Object event)来发送事件消息。那咱们来看看这个post方法。

public void post(Object event) {

Set> dispatchTypes = flattenHierarchy(event.getClass());

boolean dispatched = false;

for (Class> eventType : dispatchTypes) {

subscribersByTypeLock.readLock().lock();

try {

Set wrappers = subscribersByType.get(eventType);

if (!wrappers.isEmpty()) {

dispatched = true;

for (EventSubscriber wrapper : wrappers) {

enqueueEvent(event, wrapper);

}

}

} finally {

subscribersByTypeLock.readLock().unlock();

}

}

if (!dispatched && !(event instanceof DeadEvent)) {

post(new DeadEvent(this, event));

}

dispatchQueuedEvents();

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

publicvoidpost(Objectevent){

Set>dispatchTypes=flattenHierarchy(event.getClass());

booleandispatched=false;

for(Class>eventType:dispatchTypes){

subscribersByTypeLock.readLock().lock();

try{

Setwrappers=subscribersByType.get(eventType);

if(!wrappers.isEmpty()){

dispatched=true;

for(EventSubscriberwrapper:wrappers){

enqueueEvent(event,wrapper);

}

}

}finally{

subscribersByTypeLock.readLock().unlock();

}

}

if(!dispatched&&!(eventinstanceofDeadEvent)){

post(newDeadEvent(this,event));

}

dispatchQueuedEvents();

}

该方法就是从之前的容器subscribersByType里获取到eventType对应的观察者,然后组装成EventWithSubscriber放到队列里。

void enqueueEvent(Object event, EventSubscriber subscriber) {

eventsToDispatch.get().offer(new EventWithSubscriber(event, subscriber));

}

1

2

3

voidenqueueEvent(Objectevent,EventSubscribersubscriber){

eventsToDispatch.get().offer(newEventWithSubscriber(event,subscriber));

}

然后就是最后的dispatchQueuedEvents(),经过一层层深入进去,可以发现wrapper.handleEvent(event),其中 handleEvent方法就是最终的关键了

public void handleEvent(Object event) throws InvocationTargetException {

checkNotNull(event);

try {

method.invoke(target, new Object[] { 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;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

publicvoidhandleEvent(Objectevent)throwsInvocationTargetException{

checkNotNull(event);

try{

method.invoke(target,newObject[]{event});

}catch(IllegalArgumentExceptione){

thrownewError("Method rejected target/argument: "+event,e);

}catch(IllegalAccessExceptione){

thrownewError("Method became inaccessible: "+event,e);

}catch(InvocationTargetExceptione){

if(e.getCause()instanceofError){

throw(Error)e.getCause();

}

throwe;

}

}

就是通过Java的反射机制实现。

需要说明的是,如果没有订阅者注册到要发送的event事件上,并且该event不是DeadEvent,那么它将被包装成DeadEvent中并重新发布。也就是其中这三行代码索要做的

if (!dispatched && !(event instanceof DeadEvent)) {

post(new DeadEvent(this, event));

}

1

2

3

if(!dispatched&&!(eventinstanceofDeadEvent)){

post(newDeadEvent(this,event));

}

本文系本人原创,如要转载,请注明出处!

浏览量:

141

0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/562324.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Java jsp+servlet超市订单管理平台设计和实现【建议收藏】

🍅 作者主页:Java李杨勇 🍅 简介:Java领域优质创作者🏆、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我,都给你】 🍅 欢迎点赞 👍 收藏 ⭐留言 &#x1f…

想推翻JAVA的统治? 呵洗洗睡吧

作者:暗灭 链接:https://www.zhihu.com/question/268129932/answer/335700782 来源:知乎 已获得作者同意 本文章纯属娱乐:欢迎大家评论区留言娱乐 视频版: 2021-06-10 22-12-02文字版: “java越来越过份…

jsp java循环读取json_JAVA JSON遍历问题,求解(内附代码)

问题描述:package testJSON;import net.sf.json.JSONArray;import net.sf.json.JSONObject;public class TestJson2 {public static void main(String[] args) {String str "{"data":[{"id":"1","name":"name1"},&quo…

《零基础》MySQL删除数据表(十)

MySQL中删除数据表是非常容易操作的,但是你在进行删除表操作时要非常小心,因为执行删除命令后所有数据都会消失。 语法 以下为删除MySQL数据表的通用语法: DROP TABLE table_name ; 在命令提示窗口中删除数据表 在mysql>命令提示窗口中…

php连接mysql开发环境_PHP开发环境搭建及常用的数据库操作

PHP开发环境搭建及常用的数据库操作常见的web服务器:httpd(Apache)、nginxPHPTomcat:jsphtmlwin:IIS客户端:IE、firefox、chrome、手机浏览器(Browser)PHP开发环境:LAMPLinux Apache Mysql PHP或者LNMPLinux Nginx Mys…

《零基础》MySQL 查询数据(十二)

MySQL 数据库使用SQL SELECT语句来查询数据。 你可以通过 mysql> 命令提示窗口中在数据库中查询数据,或者通过PHP脚本来查询数据。 语法 以下为在MySQL数据库中查询数据通用的 SELECT 语法: SELECT column_name,column_name FROM table_name [WHE…

java 反射实现 工厂模式_java – 用反射实现工厂模式

我正在实施工厂模式这是我的工厂类:class ProductFactory{private HashMap m_RegisteredProducts new HashMap();public void registerProduct (String productID, Class productClass){m_RegisteredProducts.put(productID, productClass);}public Product create…

《零基础》MySQL WHERE 子句(十三)

语法 以下是 SQL SELECT 语句使用 WHERE 子句从数据表中读取数据的通用语法: SELECT field1, field2,...fieldN FROM table_name1, table_name2... [WHERE condition1 [AND [OR]] condition2..... 查询语句中你可以使用一个或者多个表,表之间使用逗号…

《零基础》MySQL UPDATE 更新(十四)

语法 以下是 UPDATE 命令修改 MySQL 数据表数据的通用 SQL 语法: UPDATE table_name SET field1new-value1, field2new-value2 [WHERE Clause] 你可以同时更新一个或多个字段。你可以在 WHERE 子句中指定任何条件。你可以在一个单独表中同时更新数据。 当你需要更…

class h5 点击后样式变化_【php】JQuery怎么实现页面刷新后保留鼠标点击addclass的样式?...

刚开始是这个效果鼠标点击之后变成了这个效果要保证实现 a 标签点击链接一个新的网址同时也要保证效果达到我目前写的网站代码 可以下载http://115.com/file/c2zlhblv看看回答感谢所以回答问题的人。比较好的办法是使用url传参数,然后根据参数判断是否有必要显示cla…

《零基础》MySQL DELETE 语句(十五)

语法 以下是 SQL DELETE 语句从 MySQL 数据表中删除数据的通用语法: DELETE FROM table_name [WHERE Clause] 如果没有指定 WHERE 子句,MySQL 表中的所有记录将被删除。你可以在 WHERE 子句中指定任何条件您可以在单个表中一次性删除记录。 当你想删除…

java 获得站点地址_JavaWeb项目里面的路径获取方法总结

仅为资源搬运,个人还未充分理解...request.getRealPath不推荐使用request.getRealPath("") 这个方法已经不推荐使用了,那代替它的是什么方法 Deprecated. As of Version 2.1 of the Java Servlet API, use ServletContext.getRealPath(java.la…

《零基础》MySQL LIKE 子句(十六)

我们知道在 MySQL 中使用 SQL SELECT 命令来读取数据, 同时我们可以在 SELECT 语句中使用 WHERE 子句来获取指定的记录。 WHERE 子句中可以使用等号 来设定获取数据的条件,如 "runoob_author RUNOOB.COM"。 但是有时候我们需要获取 runoob…

《零基础》MySQL UNION 操作符(十七)

描述 MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中。多个 SELECT 语句会删除重复的数据。 语法 MySQL UNION 操作符语法格式: SELECT expression1, expression2, ... expression_n FROM tables [WHERE conditions] UNION [ALL …

《零基础》MySQL 排序(十八)

我们知道从 MySQL 表中使用 SQL SELECT 语句来读取数据。 如果我们需要对读取的数据进行排序,我们就可以使用 MySQL 的 ORDER BY 子句来设定你想按哪个字段哪种方式来进行排序,再返回搜索结果。 语法 以下是 SQL SELECT 语句使用 ORDER BY 子句将查询…

java序列化错在哪里_Spark序列化错误:java.io.NotSerializableException

由于spark算子用到的class没有实现序列化,报错如下所示15/11/23 14:43:47 ERROR Executor: Exception in task 0.0 in stage 4.0 (TID 4)java.io.NotSerializableException: EntityMentionSerialization stack:- object not serializable (class: EntityMention, va…

《零基础》MySQL GROUP BY 语句(十九)

GROUP BY 语句根据一个或多个列对结果集进行分组。 在分组的列上我们可以使用 COUNT, SUM, AVG,等函数。 GROUP BY 语法 SELECT column_name, function(column_name) FROM table_name WHERE column_name operator value GROUP BY column_name; 实例演示 本章节实例使用到了…

字谜 java_java - 2字的字谜 - 堆栈内存溢出

我正在练习java和一些算法,所以我想创建一个程序来查看2个单词是否是彼此的字谜。 我的方法是使用快速排序对单词进行排序,然后查看它们是否匹配。 我测试了我的quicksort功能,它似乎工作。 也许我的字谜功能错了? 我测试了我的代…

基于java jsp+mybatis+Spring+的SSM二手交易网站设计实现

🍅 作者主页:Java李杨勇 🍅 简介:Java领域优质创作者🏆、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我,都给你】 🍅 欢迎点赞 👍 收藏 ⭐留言 &#x1f…

《零基础》MySQL 连接的使用(二十)

在前几章节中,我们已经学会了如何在一张表中读取数据,这是相对简单的,但是在真正的应用中经常需要从多个数据表中读取数据。 本章节我们将向大家介绍如何使用 MySQL 的 JOIN 在两个或多个表中查询数据。 你可以在 SELECT, UPDATE 和 DELETE…