EventBus是消息中间件的设计思想,在此设计中有三个非常重要的角色(Bus、Registry、Dispatcher),Bus主要负责提供给外部使用的操作方法;Registry注册表用来整理记录所有注册在EventBus上的Subscriber;Dispatcher主要负责对Subscriber消息进行推送(用反射的方式执行方法),考虑到程序的灵活性,Dispatcher方法也提供了Executor的多态方式。
示例代码如下:
java
public interface Bus {
void register(Object subscriber);
void unregister(Object subscriber);
void post(Object event);
void post(Object event,String topic);
void close();
String getBusName();
}
java
import java.lang.reflect.Method;
public interface EventContext {
String getSource();
Object getSubscriber();
Method getSubscribe();
Object getEvent();
}
java
public interface EventExceptionHandler {
void handle(Throwable cause,EventContext context);
}
java
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(METHOD)
public @interface Subscribe {
String topic() default "default-topic";
}
java
public class Subscriber {
private final Object subscribeObject;
private final Object subscribeMethod;
private boolean disable=false;
public Subscriber(Object subscribeObject,Object subscribeMethod) {
this.subscribeObject=subscribeObject;
this.subscribeMethod=subscribeMethod;
}
public Object getSubscribeObject() {
return subscribeObject;
}
public Object getSubscribeMethod() {
return subscribeMethod;
}
public boolean isDisable() {
return disable;
}
public void setDisable(boolean disable) {
this.disable = disable;
}
}
java
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
class Registry {
private final ConcurrentHashMap<String,ConcurrentLinkedQueue<Subscriber>> subscriberContainer=new ConcurrentHashMap<>();
private List<Method> getSubscribeMethods(Object subscriber){
final List<Method> methods=new ArrayList<>();
Class<?> type=subscriber.getClass();
while(type!=null) {
Method[] declaredMethods=type.getDeclaredMethods();
Arrays.stream(declaredMethods).filter(m->m.isAnnotationPresent(Subscribe.class)
&&m.getParameterCount()==1
&&m.getModifiers()==Modifier.PUBLIC)
.forEach(methods::add);
type=type.getSuperclass();
}
return methods;
}
private void tierSubscriber(Object subscriber,Method method) {
final Subscribe subscribe=method.getDeclaredAnnotation(Subscribe.class);
String topic=subscribe.topic();
this.subscriberContainer.computeIfAbsent(topic, key->new ConcurrentLinkedQueue());
this.subscriberContainer.get(topic).add(new Subscriber(subscriber,method));
}
public void bind(Object subscriber) {
List<Method> subscribeMethods=getSubscribeMethods(subscriber);
subscribeMethods.forEach(m->tierSubscriber(subscriber,m));
}
public void unbind(Object subscriber) {
this.subscriberContainer.forEach((key,queue)->queue.forEach(s->{
if(s.getSubscribeObject()==subscriber) {
s.setDisable(true);
}
}));
}
public ConcurrentLinkedQueue<Subscriber> scanScriber(String topic) {
return this.subscriberContainer.get(topic);
}
}
java
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
public class Dispatcher {
private final Executor executorService;
private final EventExceptionHandler exceptionHandler;
public static final Executor SEQ_EXECUTOR_SERVICE=SeqExecutorService.INSTANCE;
public static final Executor PER_THREAD_EXECUTOR_SERVICE=PerThreadExecutorService.INSTANCE;
private Dispatcher(Executor executorService,EventExceptionHandler handler) {
this.executorService=executorService;
this.exceptionHandler=handler;
}
private void realInvokeSubscribe(Subscriber subscriber,Object event,Bus bus) {
Method method=(Method) subscriber.getSubscribeMethod();
Object object=subscriber.getSubscribeObject();
this.executorService.execute(()->{
try {
method.invoke(object, event);
} catch (Exception e) {
if(null!=exceptionHandler) {
exceptionHandler.handle(e, new BaseEventContext(bus.getBusName(),subscriber,event));
}
e.printStackTrace();
}
});
}
public void dispatch(Bus bus, Registry registry,Object event, String topic) {
ConcurrentLinkedQueue<Subscriber> subscribers=registry.scanScriber(topic);
if(null==subscribers) {
if(exceptionHandler!=null) {
exceptionHandler.handle(new IllegalArgumentException("The topic "+topic+" not bind yet"),
new BaseEventContext(bus.getBusName(),null,event));
}
return ;
}
subscribers.stream().filter(subscriber->!subscriber.isDisable())
.filter(subscriber->{
Method method=(Method) subscriber.getSubscribeMethod();
Class<?> aClass=method.getParameterTypes()[0];
return (aClass.isAssignableFrom(event.getClass()));
}).forEach(subscriber->this.realInvokeSubscribe(subscriber, event, bus));
}
public void close() {
if(this.executorService instanceof ExecutorService) {
((ExecutorService)this.executorService).shutdown();
}
}
static Dispatcher newDispatcher(EventExceptionHandler handler,Executor executor) {
return new Dispatcher(executor,handler);
}
static Dispatcher seqDispatcher(EventExceptionHandler handler) {
return new Dispatcher(SEQ_EXECUTOR_SERVICE,handler);
}
static Dispatcher perThreadDispatcher(EventExceptionHandler handler) {
return new Dispatcher(PER_THREAD_EXECUTOR_SERVICE,handler);
}
private static class SeqExecutorService implements Executor{
private final static SeqExecutorService INSTANCE=new SeqExecutorService();
@Override
public void execute(Runnable command) {
command.run();
}
}
private static class PerThreadExecutorService implements Executor{
private final static PerThreadExecutorService INSTANCE=new PerThreadExecutorService();
@Override
public void execute(Runnable command) {
new Thread(command).start();
}
}
private static class BaseEventContext implements EventContext{
private final String eventBusName;
private final Subscriber subscriber;
private final Object event;
private BaseEventContext(String eventBusName,Subscriber subscriber,Object event) {
this.eventBusName=eventBusName;
this.subscriber=subscriber;
this.event=event;
}
@Override
public String getSource() {
return this.eventBusName;
}
@Override
public Object getSubscriber() {
return this.subscriber!=null?subscriber.getSubscribeObject():null;
}
@Override
public Method getSubscribe() {
return this.subscriber==null?(Method)subscriber.getSubscribeMethod():null;
}
@Override
public Object getEvent() {
return this.event;
}
}
}
java
import java.util.concurrent.Executor;
public class EventBus implements Bus{
private final Registry resitry=new Registry();
private String busName;
private static final String DEFAULT_BUS_NAME="default";
private static final String DEFAULT_TOPIC="default-topic";
private final Dispatcher dispatcher;
public EventBus() {
this(DEFAULT_BUS_NAME,null,Dispatcher.SEQ_EXECUTOR_SERVICE);
}
public EventBus(String busName) {
this(busName,null,Dispatcher.SEQ_EXECUTOR_SERVICE);
}
EventBus(String busName,EventExceptionHandler handler,Executor executor){
this.busName=busName;
this.dispatcher=Dispatcher.newDispatcher(handler, executor);
}
public EventBus(EventExceptionHandler exceptionHandler) {
this(DEFAULT_BUS_NAME,exceptionHandler,Dispatcher.SEQ_EXECUTOR_SERVICE);
}
@Override
public void register(Object subscriber) {
this.resitry.bind(subscriber);
}
@Override
public void unregister(Object subscriber) {
this.resitry.unbind(subscriber);
}
@Override
public void post(Object event) {
this.post(event,DEFAULT_TOPIC);
}
@Override
public void post(Object event, String topic) {
this.dispatcher.dispatch(this, resitry, event, topic);
}
@Override
public void close() {
this.dispatcher.close();
}
@Override
public String getBusName() {
return this.busName;
}
}
java
import java.util.concurrent.ThreadPoolExecutor;
public class AsyncEventBus extends EventBus{
AsyncEventBus(String busName,EventExceptionHandler handler,ThreadPoolExecutor executor){
super(busName,handler,executor);
}
public AsyncEventBus(String busName,ThreadPoolExecutor threadPoolExecutor) {
this(busName, null, threadPoolExecutor);
}
public AsyncEventBus(ThreadPoolExecutor executor) {
this("default-async",null,executor);
}
public AsyncEventBus(EventExceptionHandler handler,ThreadPoolExecutor executor) {
this("default-async",handler,executor);
}
}
java
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class SimpleScriberTest {
@Subscribe
public void methodA(String message) {
System.out.println("==SimpleSubscriber==methodA=="+message);
}
@Subscribe(topic="test")
public void methodB(String message) {
System.out.println("==SimpleSubscriber==methodB=="+message);
}
public static void main(String[] args) {
//Bus bus=new EventBus("TestBus");
Bus bus=new AsyncEventBus("TestBus",(ThreadPoolExecutor)Executors.newFixedThreadPool(10));
bus.register(new SimpleScriberTest());
bus.post("Hello");
System.out.println("__________________");
bus.post("Hello", "test");
}
}