一、进程间(服务间)的消息发送/事件通知
二、进程内的消息发送/事件通知
不使用EventBus时,进程内程序,模块与模块之间调用,是直接调用的方式,这样的缺点是:
①模块之间耦合度高,不易扩展
②如果存在多个依赖关系,采用直接调用,代码难以维护
使用EventBus的目的是进行解耦(事件源与事件处理者解耦):
- 模块A(进程A)向eventbus发送事件,模块B提前注册自己感兴趣的事件,当此类事件产生时,eventbus会push事件给对应的模块。
- 模块B随后进行处理
- 模块B处理完后,如果想要发送给模块A处理结果,可以将结果以一个新的事件发送到eventbus上,模块B这时作为监听者,接收模块B的处理结果,request与response是两个不同的、单独的event
- 也就是同一个模块即可以是事件的发送者,也可以是其他事件的监听者
三、Google EventBus
1、使用特性
- subscruber对象是一个普通类
- subscribe方法必须是public的并且是void的(即使方法有返回值,google eventbus也不会处理返回值)
- subscribe方法必须只有一个参数,多个参数无法识别,如果需要多个参数,可以将参数进行封装
- subscribe方法必须被@Subscrive注解修饰
2、简单使用方式
1)编写向eventbus注册的listener
- listener的回调方法通过@Subscribe注解进行声明:通过注解方式告诉eventbus,这是一个listener
- 通过listener的调方法的参数列表,告诉eventbus,如果有方法参数类型的event,就push给这个listener
- 方法参数只能有一个,多个参数无法识别,如果需要多个参数,可以将参数进行封装
package com.mzj.guava.eventbus.listeners;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleListener {
private final static Logger LOGGER = LoggerFactory.getLogger(SimpleListener.class);
/**
* 通过注解方式告诉eventbus,这是一个listener+参数是String类型的:告诉eventbus,如果有String类型的event,就push给这个listener
*
* google guava的eventbus的listener,方法参数只能有一个,多个参数无法识别,如果需要多个参数,可以将参数进行封装
* @param event
*/
@Subscribe
public void doAction(final String event) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Received event [{}] and will take a action", event);
}
}
}
2)创建eventbus,并向其注册listener、发送事件
package com.mzj.guava.eventbus;
import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.SimpleListener;
public class SimpleEventBusExample {
public static void main(String[] args) {
final EventBus eventBus = new EventBus();
eventBus.register(new SimpleListener());
System.out.println("post the simple event...");
eventBus.post("simple event");
}
}
3)测试
4)unregister
说明:unregister后的listener不再会监听消息
eventbus.unregister(Object listener);
3、一个类中多个监听回调方法
package com.mzj.guava.eventbus.listeners;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 一个类中多个监听回调方法
*/
public class MultipleEventListener {
private final static Logger LOGGER = LoggerFactory.getLogger(MultipleEventListener.class);
@Subscribe
public void task1(final String event) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Received event [{}] and will take a action by ==task1==", event);
}
}
@Subscribe
public void task2(final String event) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Received event [{}] and will take a action by ==task2==", event);
}
}
@Subscribe
public void intTask(final Integer event) {//这里的参数不能时int,而是integer
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Received event [{}] and will take a action by ==intTask==", event);
}
}
}
注意:基本类型不能作为listener方法的参数,需要使用对应的包装类型
测试类:
package com.mzj.guava.eventbus;
import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.MultipleEventListener;
import com.mzj.guava.eventbus.listeners.SimpleListener;
/**
* 一个类中多个监听回调方法
*/
public class MultipleEventBusExample {
public static void main(String[] args) {
final EventBus eventBus = new EventBus();
eventBus.register(new MultipleEventListener());
System.out.println("post the string event...");
eventBus.post("string event");
System.out.println("post the int event...");
eventBus.post(123);
}
}
执行结果:
4、Listener的继承
AbstractListener.java
package com.mzj.guava.eventbus.listeners;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* listener继承
*/
public abstract class AbstractListener {
private final static Logger LOGGER = LoggerFactory.getLogger(MultipleEventListener.class);
@Subscribe
public void commonTask(final String event) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Received event [{}] and will take a handle by {}.{}", event,this.getClass().getSimpleName(),"commonTask");
}
}
}
BaseListener.java extends AbstractListener.java
package com.mzj.guava.eventbus.listeners;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BaseListener extends AbstractListener {
private final static Logger LOGGER = LoggerFactory.getLogger(BaseListener.class);
@Subscribe
public void baseTask(final String event) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Received event [{}] and will take a handle by {}.{}", event,this.getClass().getSimpleName(),"baseTask");
}
}
}
ConcreteListener.java extends BaseListener.java
package com.mzj.guava.eventbus.listeners;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConcreteListener extends BaseListener {
private final static Logger LOGGER = LoggerFactory.getLogger(ConcreteListener.class);
@Subscribe
public void conTask(final String event) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Received event [{}] and will take a handle by {}.{}", event,this.getClass().getSimpleName(),"conTask");
}
}
}
测试类:
package com.mzj.guava.eventbus;
import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.ConcreteListener;
import com.mzj.guava.eventbus.listeners.MultipleEventListener;
/**
* Listener的继承
*/
public class InheritListenerEventBusExample {
public static void main(String[] args) {
final EventBus eventBus = new EventBus();
eventBus.register(new ConcreteListener());
System.out.println("post the string event...");
eventBus.post("string event");
}
}
运行结果:
可见:继承关系的类,中符合参数列表的方法也会被调用,这种特性需要根据实际业务进行衡量,是否需要被调用。
5、Event的继承
Fruit(水果)
package com.mzj.guava.eventbus.events;
import com.google.common.base.MoreObjects;
public class Fruit {
private String name;
public Fruit(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("name", name)
.toString();
}
}
苹果 extends 水果
package com.mzj.guava.eventbus.events;
public class Apple extends Fruit {
public Apple(String name) {
super(name);
}
}
listener:
package com.mzj.guava.eventbus.listeners;
import com.google.common.eventbus.Subscribe;
import com.mzj.guava.eventbus.events.Apple;
import com.mzj.guava.eventbus.events.Fruit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Event的继承
*/
public class FruitEateListener {
private final static Logger LOGGER = LoggerFactory.getLogger(FruitEateListener.class);
@Subscribe
public void eat(Fruit event) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Fruit eat [{}]", event);
}
}
@Subscribe
public void eat(Apple event) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Apple eat [{}]", event);
}
}
}
测试类:
package com.mzj.guava.eventbus;
import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.events.Apple;
import com.mzj.guava.eventbus.events.Fruit;
import com.mzj.guava.eventbus.listeners.ConcreteListener;
import com.mzj.guava.eventbus.listeners.FruitEateListener;
/**
* Event的继承
*/
public class InheritEventsEventBusExample {
public static void main(String[] args) {
final EventBus eventBus = new EventBus();
eventBus.register(new FruitEateListener());
eventBus.post(new Apple("apple"));
System.out.println("--------");
eventBus.post(new Fruit("fruit"));
}
}
运行结果:
可见,eventbus在继承事件的通知上与面向对象特性一致
6、Exception
说明:当eventbus收到某个事件时会以push的方式将消息推送给subscribers(消息订阅者s),某个subscriber处理消息时,如果出现异常(异常向上抛到eventbus调用线程),不会影响其他subscriber
listener:
package com.mzj.guava.eventbus.listeners;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ExceptionListener {
private final static Logger LOGGER = LoggerFactory.getLogger(ExceptionListener.class);
@Subscribe
public void m1(String event){
LOGGER.info("===========m1==========",event);
}
@Subscribe
public void mException(String event){
throw new RuntimeException("测试subscribe处理过程抛出异常");
}
@Subscribe
public void m2(String event){
LOGGER.info("===========m2==========",event);
}
@Subscribe
public void m3(String event){
LOGGER.info("===========m3==========",event);
}
}
测试代码:
package com.mzj.guava.eventbus;
import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.ExceptionListener;
public class ExceptionEventBusExample {
public static void main(String[] args) {
final EventBus eventBus = new EventBus();
eventBus.register(new ExceptionListener());
eventBus.post("exception post");
}
}
运行结果:
但是~~!!,我们发现,抛出的异常目前的处理行为仅仅是在eventbus内部进行了堆栈信息打印,那么如何能使我们可以获取到异常对象呢?
答案:①实现SubscriberExceptionHandler接口,在接口的handleException方法中可以获得异常对象并且通过方法的第二个参数context可以获取异常对应产生的事件、eventbus、Subscriber等信息②创建接口实现类并注册到eventbus上
注:SubscriberExceptionHandler接口是一个Functioninterface,如果是java8,可以直接传递lambda
代码示例:
package com.mzj.guava.eventbus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.SubscriberExceptionContext;
import com.google.common.eventbus.SubscriberExceptionHandler;
import com.mzj.guava.eventbus.listeners.ExceptionListener;
public class ExceptionEventBusExample {
public static void main(String[] args) {
final EventBus eventBus = new EventBus(new ExceptionHandler());
eventBus.register(new ExceptionListener());
eventBus.post("exception post");
}
static class ExceptionHandler implements SubscriberExceptionHandler{
@Override
public void handleException(Throwable exception, SubscriberExceptionContext context) {
System.out.println(context.getEvent());
System.out.println(context.getEventBus());
System.out.println(context.getSubscriber());
System.out.println(context.getSubscriberMethod());
}
}
}
7、dead event
说明:当发送的事件没有被消费时(没有任何subscriber对其感兴趣)则此event被称为dead event,如何获取这类事件呢?
答:通过一个listener,方法声明的参数类型为DeadEvent
listener:
package com.mzj.guava.eventbus.listeners;
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;
public class DeadEventListener {
@Subscribe
public void handle1(DeadEvent event){
System.out.println("[Dead listener]"+event.getSource());
System.out.println("[Dead listener]"+event.getEvent());
}
@Subscribe
public void handle2(String event){
System.out.println("[String listener]" + event);
}
}
测试代码:
package com.mzj.guava.eventbus;
import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.DeadEventListener;
public class DeadEventBusExample {
public static void main(String[] args) {
final EventBus eventBus = new EventBus();
eventBus.register(new DeadEventListener());
eventBus.post("Hello");
eventBus.post(123);
}
}
运行结果:
四、完整代码示例
https://github.com/mazhongjia/googleguava/tree/master/src/main/java/com/mzj/guava/eventbus