这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战
使用背景
某某服务商通过nlp识别用户的发送消息判断是不是具有特别含义的信息,比如是一条询价的信息,打上标签,推送过来我们这边,然后做一些业务处理。当然消息类型是有多种,不单单只有询价类型。处理多种不同的推送信息,考虑使用观察者模式。刚好Guava中EventBus符合要求,而且使用简单。
使用方法
首先是事件处理中心
很简单,就实例了一个EventBus
public class EventBusCenter {
public static final EventBus eventBus = new EventBus();
}
定义不同的事件类型
@Data
@Builder
public class AEvent {
private String name;
private Integer id;
}
@Data
@Builder
public class BEvent {
private String name;
private Integer id;
}
@Data
@Builder
public class CEvent {
private String name;
private Integer id;
}
定义每个事件类型对应的监听器
注意,每个监听器的处理事件方法必须加上注解**@Subscribe,并且是public void,只有一个参数**。
public class AEventListener {
@Subscribe
public void handlerAEvent(AEvent aEvent) {
System.out.println("handle A Event");
}
}
public class BEventListener {
@Subscribe
public void handlerAEvent(BEvent bEvent) {
System.out.println("handle B Event");
}
}
public class CEventListener {
@Subscribe
public void handlerCEvent(CEvent cEvent) {
System.out.println("handle C Event");
}
}
测试
register方法注册监听器,post方法推送具体事件到eventbus上。
public class MainTest {
public static void main(String[] args) {
EventBusCenter.eventBus.register(new AEventListener());
EventBusCenter.eventBus.register(new BEventListener());
EventBusCenter.eventBus.register(new CEventListener());
EventBusCenter.eventBus.post(AEvent.builder().id(1).name("A").build());
EventBusCenter.eventBus.post(BEvent.builder().id(2).name("B").build());
EventBusCenter.eventBus.post(CEvent.builder().id(3).name("C").build());
}
}
使用方法很简单。但是应用到具体场景上还是不太合理。比如处理A事件的时候发生异常
@Subscribe
public void handlerAEvent(AEvent aEvent) {
System.out.println("handle A Event");
throw new RuntimeException();
}
异常会往上抛,最终被Subcriber catch。但是应用到具体场景中,发生异常后不用抛出,我有一个兜底的处理。可以使用eventbus中的事件异常处理SubscriberExceptionHandler。具体使用方法只需要创建eventbus的时候加入参数即可。
public class EventBusCenter {
public static final EventBus eventBus = new EventBus(((exception, context) -> {
System.out.println("error do something");
System.out.println(context.getEvent());
System.out.println(context.getEventBus());
System.out.println(context.getSubscriber());
}));
}
还有一个问题就是事件处理完了,该如何通知前端做相应的处理。我这边的处理专门写一个订阅事件完成通知的监听器,因为现有的架构是可以通过websocket与前端通信,不需要通过返回值通知前端。
public class MainTest {
public static void main(String[] args) {
EventBusCenter.eventBus.register(new AEventListener(EventBusCenter.eventBus));
EventBusCenter.eventBus.register(new NoticeListener());
EventBusCenter.eventBus.register(new BEventListener());
EventBusCenter.eventBus.register(new CEventListener());
EventBusCenter.eventBus.post(AEvent.builder().id(1).name("A").build());
EventBusCenter.eventBus.post(BEvent.builder().id(2).name("B").build());
EventBusCenter.eventBus.post(CEvent.builder().id(3).name("C").build());
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Notice {
String content;
}
public class NoticeListener {
@Subscribe
public void handleNotice(Notice notice) {
System.out.println(notice.getContent());
//通过webcocket通知前端
}
}
public class AEventListener {
private EventBus eventBus;
public AEventListener(EventBus eventBus) {
this.eventBus = eventBus;
}
@Subscribe
public void handlerAEvent(AEvent aEvent) {
System.out.println("handle A Event");
//事件处理完成
eventBus.post(new Notice("handle A Event success"));
}
}
参考某视频实现的一个简单eventbus
工程目录
MyBus
public interface MyBus {
void register(Object subscriber);
void post(Object event,String topic);
void post(Object event);
String getBusName();
}
MyEventBus
public class MyEventBus implements MyBus {
private String busName;
private MyEventExceptionHandler handler;
private MyDispatcher dispatcher;
private Executor executor;
public MyEventBus(String busName) {
this.busName = busName;
}
public MyEventBus(String busName, MyEventExceptionHandler handler) {
this(busName);
this.handler = handler;
}
public MyEventBus() {
this(Constant.DEFAULT_BUS_NAME, null);
}
private final MyRegister register = new MyRegister();
@Override
public void register(Object subscriber) {
this.register.bind(subscriber);
}
@Override
public void post(Object event, String topic) {
this.dispatcher.dispatcher(ExecutorParam.builder()
.event(event)
.bus(this)
.topic(topic)
.handler(handler)
.build());
}
@Override
public void post(Object event) {
this.post(event, Constant.DEFAULT_TOPIC_NAME);
}
@Override
public String getBusName() {
return this.busName;
}
}
MyRegister
/**
* 用于保存订阅者
*/
class MyRegister {
private final ConcurrentHashMap<String, ConcurrentLinkedQueue<MySubscriber>> registerTable = new ConcurrentHashMap<>();
public ConcurrentLinkedQueue<MySubscriber> getMySubscriberQueue(String topic) {
return registerTable.get(topic);
}
public void bind(Object subscribe) {
List<Method> subscriberMethod = this.getSubscriberMethod(subscribe);
subscriberMethod.forEach(v -> this.helper(v, subscribe));
}
public void unbind(Object subscribe) {
registerTable.forEach((k, q) -> {
q.forEach(v -> {
if (v.getSubcriber() == subscribe) {
v.setDisable(true);
}
});
});
}
private void helper(Method method, Object subscribe) {
MySubscribe mySubscribe = method.getDeclaredAnnotation(MySubscribe.class);
String topic = mySubscribe.topic();
registerTable.computeIfAbsent(topic, key -> new ConcurrentLinkedQueue<>());
ConcurrentLinkedQueue<MySubscriber> mySubscribers = registerTable.get(topic);
mySubscribers.add(new MySubscriber(subscribe, method, false));
}
private List<Method> getSubscriberMethod(Object subscribe) {
Class<?> aClass = subscribe.getClass();
Method[] methods = aClass.getDeclaredMethods();
List<Method> res = Arrays.stream(methods).filter(v -> {
return v.isAnnotationPresent(MySubscribe.class) &&
v.getParameterCount() == 1 && v.getModifiers() == Modifier.PUBLIC;
}).collect(Collectors.toList());
return res;
}
}
MyDispatcher
public class MyDispatcher {
private final MyRegister register = new MyRegister();
public void dispatcher(ExecutorParam param) {
ConcurrentLinkedQueue<MySubscriber> mySubscriberQueue = register.getMySubscriberQueue(param.getTopic());
if (Objects.isNull(mySubscriberQueue)) {
if (Objects.isNull(param.getHandler())) {
param.getHandler().handle(new IllegalArgumentException("topic非法"), MyEventBusContext.builder()
.event(param.getEvent())
.myEventBus(param.getBus())
.eventBusName(param.getBus().getBusName())
.build());
return;
} else {
throw new IllegalArgumentException();
}
}
mySubscriberQueue.stream().filter(v -> {
return !v.getDisable();
}).filter(s -> {
Method method = s.getSubcriberMethods();
Class<?> classz = method.getParameterTypes()[0];
return classz.isAssignableFrom(param.getEvent().getClass());
}).forEach(v -> {
this.executorMethod(v, param);
});
}
private void executorMethod(MySubscriber mySubscriber, ExecutorParam param) {
Object subscriber = mySubscriber.getSubcriber();
Method method = mySubscriber.getSubcriberMethods();
param.getExecutor().execute(() -> {
try {
method.invoke(subscriber, param.getEvent());
} catch (Exception e) {
if (Objects.nonNull(param.getHandler())) {
param.getHandler().handle(new Exception("failed"), MyEventBusContext.builder()
.event(param.getEvent())
.myEventBus(param.getBus())
.eventBusName(param.getBus().getBusName())
.build());
} else {
e.printStackTrace();
}
}
});
}
}
MySubscriber
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MySubscriber {
private Object subcriber;
private Method subcriberMethods;
private Boolean disable;
}
MyEventExceptionHandler
/**
* 事件异常处理
*/
public interface MyEventExceptionHandler {
void handle(Throwable e, MyEventBusContext context);
}
ExecutorParam
@Data
@Builder
public class ExecutorParam {
private Executor executor = DefaultExecutor.DEFAULT_EXECUTOR;
private MyEventExceptionHandler handler;
private MyRegister register;
private MyBus bus;
private Object event;
private String topic;
private static class DefaultExecutor implements Executor {
private static final DefaultExecutor DEFAULT_EXECUTOR = new DefaultExecutor();
@Override
public void execute(Runnable command) {
command.run();
}
}
}
注解MySubscribe
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MySubscribe {
String topic() default "default_topic";
}
Constant
public class Constant {
public final static String DEFAULT_BUS_NAME = "default";
public final static String DEFAULT_TOPIC_NAME = "default_topic";
}