EventBus 是 Guava 的事件处理机制,是观察者模式(生产/消费模型)的一种实现。根据发送对象的类型,查找所有匹配的订阅处理器进行处理。
1. 事件默认在当前线程直接调用处理
1.1.简单使用示例
package org.example.model.guava;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.Test;
public class EventBusTest {
@AllArgsConstructor
@Data
public static class Person {
private String name;
private int age;
}
public static class SimpleListener {
@Subscribe
// Subscribe注解标注的方法为订阅处理器,处理String类型的事件
public void doSomething(String event) {
System.out.println(this.getClass().getSimpleName() + ":" + event);
}
@Subscribe
public void doSomething2(String event) {
System.out.println(this.getClass().getSimpleName() + "2:" + event);
}
@Subscribe
// 订阅处理Person类型的事件
public void personListener(Person person) {
System.out.println(this.getClass().getSimpleName() + ":" + person);
}
}
@Test
public void test() {
EventBus eventBus = new EventBus();
// 注解监听者
eventBus.register(new SimpleListener());
// 发送事件
eventBus.post("发送字符串!");
eventBus.post(new Person("张三", 20));
}
}
执行结果:
SimpleListener2:发送字符串!
SimpleListener:发送字符串!
SimpleListener:EventBusTest.Person(name=张三, age=20)
1.2 监听器有继承关系时
监听器有继承关系时,父类和子类所有被Subscribe注解标记的方法都会被注册为事件订阅处理器。
package org.example.model.guava;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.Test;
public class EventBusTest {
@AllArgsConstructor
@Data
public static class Person {
private String name;
private int age;
}
public static abstract class absListener {
@Subscribe
public void doSomething2(String event) {
System.out.println(this.getClass().getSimpleName() + "2:" + event);
}
@Subscribe
public void personListener(Person person) {
System.out.println(this.getClass().getSimpleName() + ":" + person);
}
}
public static class SimpleListener extends absListener {
@Subscribe
public void doSomething(String event) {
System.out.println(this.getClass().getSimpleName() + ":" + event);
}
}
@Test
public void test() {
EventBus eventBus = new EventBus();
eventBus.register(new SimpleListener());
eventBus.post("发送字符串!");
eventBus.post(new Person("张三", 20));
}
}
执行结果:
SimpleListener:发送字符串!
SimpleListener2:发送字符串!
SimpleListener:EventBusTest.Person(name=张三, age=20)
1.3 事件对象有继承关系时
事件对象有继承关系时,由于子类型可以转换为父类型,订阅父类型的处理器可以订阅到子类型进行处理:
package org.example.model.guava;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.SuperBuilder;
import org.junit.Test;
public class EventBusTest {
@AllArgsConstructor
@SuperBuilder
@Data
public static class Person {
private String name;
private int age;
}
@SuperBuilder()
public static class Man extends Person {
}
public static class SimpleListener {
@Subscribe
public void personListener(Person person) {
System.out.println(this.getClass().getSimpleName() + "---Person:" + person);
}
@Subscribe
public void manListener(Man man) {
System.out.println(this.getClass().getSimpleName() + "---Man:" + man);
}
}
@Test
public void test() {
EventBus eventBus = new EventBus();
eventBus.register(new SimpleListener());
eventBus.post(Man.builder().name("张三").age(20).build());
}
}
执行结果:
SimpleListener---Man:EventBusTest.Person(name=张三, age=20)
SimpleListener---Person:EventBusTest.Person(name=张三, age=20)
1.4 订阅处理器执行异常不会影响其他订阅处理器执行
package org.example.model.guava;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import org.junit.Test;
public class EventBusTest {
public static class SimpleListener {
@Subscribe
public void listener1(String event) {
System.out.println(this.getClass().getSimpleName() + "---1111:" + event);
throw new RuntimeException();
}
@Subscribe
public void listener2(String event) {
System.out.println(this.getClass().getSimpleName() + "---2222:" + event);
}
}
@Test
public void test() {
EventBus eventBus = new EventBus();
eventBus.register(new SimpleListener());
eventBus.post("异常测试");
}
}
执行结果:
listener1异常并没有影响listener2的执行。
1.5 自定义异常处理
在创建EventBus时,可以传入自定义的异常处理方法:
package org.example.model.guava;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import org.junit.Test;
public class EventBusTest {
public static class SimpleListener {
@Subscribe
public void listener1(String event) {
System.out.println(this.getClass().getSimpleName() + "---1111:" + event);
throw new RuntimeException();
}
@Subscribe
public void listener2(String event) {
System.out.println(this.getClass().getSimpleName() + "---2222:" + event);
}
}
@Test
public void test() {
// 创建eventbus时,自定义异常处理方法
EventBus eventBus = new EventBus((exception, context) ->
{
// 异常时,可以从context获取eventbus, event,监听器和监听器中发送异常的的事件订阅方法
System.out.println("ExceptionHandler EventBus:" + context.getEventBus());
System.out.println("ExceptionHandler Event:" + context.getEvent());
System.out.println("ExceptionHandler Subscriber:" + context.getSubscriber());
System.out.println("ExceptionHandler SubscriberMethod:" + context.getSubscriberMethod());
});
eventBus.register(new SimpleListener());
eventBus.post("异常测试");
}
}
1.6 DeadEvent处理没有订阅的事件
package org.example.model.guava;
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import org.junit.Test;
public class EventBusTest {
public static class SimpleListener {
@Subscribe
// 匹配不到任何事件订阅,会使用DeadEvent处理
public void listener1(DeadEvent event) {
System.out.println(this.getClass().getSimpleName() + ":" + event);
}
}
@Test
public void test() {
EventBus eventBus = new EventBus();
eventBus.register(new SimpleListener());
eventBus.post("没有被订阅的消息");
}
}
1.7 使用eventbus和nio监听文件变化的示例
package org.example.model.guava;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.*;
import java.util.Optional;
public class EventBusTest {
@SneakyThrows
@Test
public void test() {
// 创建eventbus时,自定义异常处理方法
EventBus eventBus = new EventBus();
eventBus.register(new FileChangeListener());
DirectoryMonitor monitor = new DirectoryMonitor(eventBus, "D:\\java\\test1\\apache1");
monitor.start();
}
@RequiredArgsConstructor
public static class DirectoryMonitor {
private final EventBus eventBus;
private final String path;
private WatchService watchService;
private volatile boolean start;
public void start() throws IOException {
watchService = FileSystems.getDefault().newWatchService();
Path monitorPath = Paths.get(path);
monitorPath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
start = true;
WatchKey watchKey = null;
while (start) {
try {
watchKey = watchService.take();
watchKey.pollEvents().forEach(eventBus::post);
} catch (InterruptedException e) {
} finally {
Optional.ofNullable(watchKey).ifPresent(WatchKey::reset);
}
}
}
public void stop() throws IOException {
start = false;
Thread.currentThread().interrupt();
watchService.close();
}
}
public static class FileChangeListener {
@Subscribe
public void onChange(WatchEvent<?> event) {
System.out.println(event.context() + "--" + event.kind());
}
}
}
在目录 D:\java\test1\apache1 下创建/修改/删除 1.txt,控制台打印如下:
1.txt--ENTRY_CREATE
1.txt--ENTRY_MODIFY
1.txt--ENTRY_DELETE
2. 使用线程池异步处理事件
2.1 使用AsyncEventBus发送异步事件
AsyncEventBus创建时,需要传入使用的线程池:
package org.example.model.guava;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import lombok.SneakyThrows;
import org.junit.Test;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class EventBusTest {
@SneakyThrows
@Test
public void test() {
// 异步事件处理需要提供线程池
EventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(2));
eventBus.register(new SimpleListener());
eventBus.post("需要异步处理的事件");
// 延迟4秒,等待事件处理结束
TimeUnit.SECONDS.sleep(4);
}
public static class SimpleListener {
@SneakyThrows
@Subscribe
public void listener1(String event) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "--listener1 start: " + event);
TimeUnit.SECONDS.sleep(3);
System.out.println(threadName + "--listener1 end: " + event);
}
@SneakyThrows
@Subscribe
public void listener2(String event) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "--listener2 start: " + event);
TimeUnit.SECONDS.sleep(2);
System.out.println(threadName + "--listener2 end: " + event);
}
}
}
listener1和listener2会使用不同的线程处理,打印结果如下:
pool-1-thread-1--listener2 start: 需要异步处理的事件
pool-1-thread-2--listener1 start: 需要异步处理的事件
pool-1-thread-1--listener2 end: 需要异步处理的事件
pool-1-thread-2--listener1 end: 需要异步处理的事件