传统上,Java的进程内事件分发都是通过发布者和订阅者之间的显式注册实现的。设计EventBus就是为了取代这种显示注册方式,使组件间有了更好的解耦。EventBus不是通用型的发布-订阅实现,不适用于进程间通信。
EventBus是Guava的事件处理机制,是设计模式中的观察者模式(生产/消费者编程模型)的优雅实现。对于事件监听和发布订阅模式,EventBus非常优雅使用起来也非常的简单,这个可不是吹的是真的非常的简单。
依赖
com.google.guava
guava
20.0
package org.demo.spring.event.eventbus;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.SubscriberExceptionContext;
import com.google.common.eventbus.SubscriberExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 事件总线工厂
*
*/
@Slf4j
public class EventBusFactory {
// 同步事件总线
private volatile static EventBus eventBus;
// 异步事件总线
private volatile static AsyncEventBus asyncEventBus;
private static ThreadPoolExecutor threadPoolExecutor;
/**
* 默认使用异步事件总线
* @return
*/
public static EventBus getEventBus() {
return getEventBus(true);
}
/**
* 可以采用静态内部类实现单例
*/
public static EventBus getEventBus(boolean async) {
if (async) {
if (asyncEventBus == null) {
synchronized (AsyncEventBus.class) {
// 双重检查,避免并发访问创建多个对象问题
if (asyncEventBus == null) {
asyncEventBus = new AsyncEventBus(getEventBusThreadPool());
}
}
}
return asyncEventBus;
} else {
if (eventBus == null) {
synchronized (EventBus.class) {
// 双重检查,避免并发访问创建多个对象问题
if (eventBus == null) {
eventBus = new EventBus(DefaultExceptionHandler.INSTANCE);
}
}
}
return eventBus;
}
}
private static ThreadPoolExecutor getEventBusThreadPool() {
if (threadPoolExecutor == null) {
// 当队列满时,任务直接丢弃,避免出错
threadPoolExecutor = new ThreadPoolExecutor(20,
120,
60,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(100000),
new NamedThreadFactory("EventBus_BlockThreadPool"),
new ThreadPoolExecutor.DiscardPolicy());
}
return threadPoolExecutor;
}
/**
* 默认异常处理者
*
*/
static class DefaultExceptionHandler implements SubscriberExceptionHandler {
public static final DefaultExceptionHandler INSTANCE = new DefaultExceptionHandler();
// 是否有错
private ThreadLocal errorFlag = new ThreadLocal() {
@Override
protected Boolean initialValue() {
return false;
}
};
@Override
public void handleException(Throwable exception, SubscriberExceptionContext context) {
if (!errorFlag.get()) {
errorFlag.set(true);
}
Logger logger = logger(context);
logger.error(message(context), exception);
}
public boolean hasError() {
return errorFlag.get();
}
public void clearErrorFlag() {
errorFlag.remove();
}
private static Logger logger(SubscriberExceptionContext context) {
return LoggerFactory.getLogger(DefaultExceptionHandler.class);
}
private static String message(SubscriberExceptionContext context) {
Method method = context.getSubscriberMethod();
return "Exception thrown by subscriber method " + method.getName() + '('
+ method.getParameterTypes()[0].getName() + ')' + " on subscriber " + context.getSubscriber()
+ " when dispatching event: " + context.getEvent();
}
}
/**
* 返回并执行删除
* @return true 表示有错误,false表示没有错误
*/
public static boolean getAndRemoveErrorFlag() {
try {
return DefaultExceptionHandler.INSTANCE.hasError();
} finally {
DefaultExceptionHandler.INSTANCE.clearErrorFlag();
}
}
}
package org.demo.spring.event.eventbus;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 复制来源 org.apache.dubbo.common.utils -NamedThreadFactory
*/
public class NamedThreadFactory implements ThreadFactory {
protected static final AtomicInteger POOL_SEQ = new AtomicInteger(1);
protected final AtomicInteger mThreadNum;
protected final String mPrefix;
protected final boolean mDaemon;
protected final ThreadGroup mGroup;
public NamedThreadFactory() {
this("pool-" + POOL_SEQ.getAndIncrement(), false);
}
public NamedThreadFactory(String prefix) {
this(prefix, false);
}
public NamedThreadFactory(String prefix, boolean daemon) {
this.mThreadNum = new AtomicInteger(1);
this.mPrefix = prefix + "-thread-";
this.mDaemon = daemon;
SecurityManager s = System.getSecurityManager();
this.mGroup = s == null ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
}
public Thread newThread(Runnable runnable) {
String name = this.mPrefix + this.mThreadNum.getAndIncrement();
Thread ret = new Thread(this.mGroup, runnable, name, 0L);
ret.setDaemon(this.mDaemon);
return ret;
}
public ThreadGroup getThreadGroup() {
return this.mGroup;
}
}
/**
* 事件
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IEvent implements Serializable {
private static final long serialVersionUID = -8819029849121087074L;
private Map map;
}
/**
* 事件监听器
*/
@Component
@Slf4j
public class IEventListener {
@PostConstruct
private void init() {
// 注册事件监听器
EventBusFactory.getEventBus().register(this);
}
@Subscribe
@AllowConcurrentEvents
public final void listen(IEvent iEvent) {
log.info("接受订阅消息:{}", iEvent);
}
}
测试:
/**
* 事件发布测试
* 由于是自己搭建的springboot项目,启动非常快,有时测试就直接用定时任务,不写单侧了
*/
@Component
public class TestJob {
@Scheduled(cron = "*/5 * * * * ?")
public void cronJob() {
IEvent iEvent = new IEvent();
Map map = new HashMap() {
{
put("a", "123");
put("b", "456");
}
};
iEvent.setMap(map);
EventBusFactory.getEventBus().post(iEvent);
}
}
参考:
Google Guava EventBus
Google Guava]-事件总线