背景说明
这两天有个需求,遇到个小问题:大项目A分为a、b、c、d几个小模块,假设a是Controller层,b是Serice层,c是对外提供的接口,b引了c的pom,c没有引b的pom,现在代码入口是c,然后需要调用b中的接口和方法,但因为c没有引b的pom,所以没办法注入Bean
- 简单的方法是c引入b的pom就可以,但尽量还是不要改代码模块之间的pom相互引入;
- c中声明Service,b中实现Service,然后在实现类中注入要调用b的接口bean,但这种又会导致文件混乱
- 通过MQ传递消息,但由于是一个项目中的数据,再接入MQ感觉更复杂了
然后就找找有没有其他什么方法,发现代码中有事件发布订阅功能,一测试还真能达到我想要的效果:c发布一个事件,然后在b中写事件订阅者,去调用b中的接口和方法去继续处理
由于之前对事件发布订阅没了解过,所以这里记录下
在Java中,EventBus 是一种用于在应用程序的不同部分之间通信的模式,它允许对象发布(发送)事件,而其他对象可以订阅(接收)这些事件。这种方式有助于解耦组件,提高代码的可维护性和可测试性。在Java中,有多种库和框架实现了EventBus模式,比如Google的Guava库中的EventBus、Spring框架中的ApplicationEvent和ApplicationContextEvent,以及Apache的Kafka等
guava中的EventBus使用
这里拿guava的使用和源码来看看
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.4.0-jre</version>
</dependency>
简单demo
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
public class TestClass {
public static void main(String[] args) {
// 声明个EventBus,给个标识(相当于起个名字)
// 也可以不起名字用默认值"default"
EventBus eventBus = new EventBus("eventBusTest");
// 注册事件订阅者TestEvent,会去这个类下找所有的@Subscribe注解(一会看源码)
eventBus.register(new TestEvent());
// 发布string事件,会发现listenStr1和listenStr5被通知
eventBus.post("1");
//控制台打印 1:listenStr5
// 1:listenStr1
// 发布Integer事件,会发现listenStr2和listenStr6被通知
eventBus.post(100);
//控制台打印 100:listenStr2
// 100:listenStr6
// 发布Boolean事件,会发现listenStr3被通知
eventBus.post(true);
//控制台打印 true:listenStr3
// 发布Double事件,会发现listenStr4和listenStr7被通知
eventBus.post(12.54);
//控制台打印 12.54:listenStr7
// 12.54:listenStr4
}
}
class TestEvent {
//只有使用@Subscribe才会被通知
//依赖的是方法参数和投递的对象参数一致
//订阅 参数是String类型的事件
@Subscribe
public void listenStr1(String str){
System.out.println(str+":listenStr1");
}
//订阅 参数是Integer类型的事件
@Subscribe
public void listenStr2(Integer str){
System.out.println(str+":listenStr2");
}
//订阅 参数是Boolean类型的事件
@Subscribe
public void listenStr3(Boolean str){
System.out.println(str+":listenStr3");
}
//订阅 参数是Double类型的事件
@Subscribe
public void listenStr4(Double str){
System.out.println(str+":listenStr4");
}
//订阅 参数是String类型的事件,看和方法1是不是会被同时"调用"
@Subscribe
public void listenStr5(String str){
System.out.println(str+":listenStr5");
}
//订阅 参数是Integer类型的事件,看和方法2是不是会被同时"调用"
@Subscribe
public void listenStr6(Integer str){
System.out.println(str+":listenStr6");
}
//订阅 参数是Double类型的事件,看和方法4是不是会被同时"调用"
@Subscribe
public void listenStr7(Double str){
System.out.println(str+":listenStr7");
}
}
以上就是guava中的EventBus的简单使用,接下来看一下源码都做了什么才能实现的这样的功能
eventBus属性值
private final String identifier; // 名字
private final Executor executor; // 线程
private final SubscriberExceptionHandler exceptionHandler; // 异常处理器
private final SubscriberRegistry subscribers; // 订阅注册对象
private final Dispatcher dispatcher; // 调度
注册
// 点击进入register方法如下
public void register(Object object) {
this.subscribers.register(object);
}
void register(Object listener) {
// 核心方法反射获取@Subscribe该注解的方法,并将传入的class进行分类
Multimap<Class<?>, Subscriber> listenerMethods = this.findAllSubscribers(listener);
Collection eventMethodsInListener;
CopyOnWriteArraySet eventSubscribers;
for(Iterator var3 = listenerMethods.asMap().entrySet().iterator(); var3.hasNext(); eventSubscribers.addAll(eventMethodsInListener)) {
Map.Entry<Class<?>, Collection<Subscriber>> entry = (Map.Entry)var3.next();
Class<?> eventType = (Class)entry.getKey();
eventMethodsInListener = (Collection)entry.getValue();
eventSubscribers = (CopyOnWriteArraySet)this.subscribers.get(eventType);
if (eventSubscribers == null) {
CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet();
eventSubscribers = (CopyOnWriteArraySet)MoreObjects.firstNonNull((CopyOnWriteArraySet)this.subscribers.putIfAbsent(eventType, newSet), newSet);
}
}
}
// findAllSubscribers方法
private Multimap<Class<?>, Subscriber> findAllSubscribers(Object listener) {
Multimap<Class<?>, Subscriber> methodsInListener = HashMultimap.create();
Class<?> clazz = listener.getClass();
// 获取带@Subscribe注解的方法
UnmodifiableIterator var4 = getAnnotatedMethods(clazz).iterator();
while(var4.hasNext()) {
Method method = (Method)var4.next();
// 获取带注解的方法,并把方法的第一个参数类型作为key,value是这个方法
// 为每个方法创建一个Subscriber对象,并按事件类型存入methodsInListener中
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> eventType = parameterTypes[0];
methodsInListener.put(eventType, Subscriber.create(this.bus, listener, method));
}
return methodsInListener;
}
调用
// 主方法调用post
public void post(Object event) {
// 通过event的获取对应的订阅者集合,
Iterator<Subscriber> eventSubscribers = this.subscribers.getSubscribers(event);
// 通知事件订阅者
if (eventSubscribers.hasNext()) {
this.dispatcher.dispatch(event, eventSubscribers);
} else if (!(event instanceof DeadEvent)) {
// the event had no subscribers and was not itself a DeadEvent
this.post(new DeadEvent(this, event));
}
}
// getSubscribers方法
Iterator<Subscriber> getSubscribers(Object event) {
// 获取事件的所有继承链类型(如 event.getClass() 及其父类、接口等)
ImmutableSet<Class<?>> eventTypes = flattenHierarchy(event.getClass());
//遍历这些类型,从 subscribers 映射中获取对应的订阅者集合
List<Iterator<Subscriber>> subscriberIterators = Lists.newArrayListWithCapacity(eventTypes.size());
UnmodifiableIterator var4 = eventTypes.iterator();
// 将每个类型的订阅者迭代器加入列表
while(var4.hasNext()) {
Class<?> eventType = (Class)var4.next();
CopyOnWriteArraySet<Subscriber> eventSubscribers = (CopyOnWriteArraySet)this.subscribers.get(eventType);
if (eventSubscribers != null) {
subscriberIterators.add(eventSubscribers.iterator());
}
}
return Iterators.concat(subscriberIterators.iterator());
}
// 回到主方法dispatcher.dispatch进行调用
@Override
void dispatch(Object event, Iterator<Subscriber> subscribers) {
checkNotNull(event);
checkNotNull(subscribers);
Queue<Event> queueForThread = queue.get();
queueForThread.offer(new Event(event, subscribers));
if (!dispatching.get()) {
dispatching.set(true);
try {
Event nextEvent;
while ((nextEvent = queueForThread.poll()) != null) {
while (nextEvent.subscribers.hasNext()) {
// 核心方法调用
nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
}
}
} finally {
dispatching.remove();
queue.remove();
}
}
}
// dispatchEvent方法中可以看到把任务直接丢到线程池
void dispatchEvent(final Object event) {
executor.execute(
new Runnable() {
@Override
public void run() {
try {
invokeSubscriberMethod(event);
} catch (InvocationTargetException e) {
bus.handleSubscriberException(e.getCause(), context(event));
}
}
});
}
项目代码使用示例
源码研究完了,但代码中肯定不会像上面demo一样那样去使用,具体怎么使用,可以依据各自的代码去决定,下面只说一下我项目中怎么使用的
配置类
ApplicationContextUtil工具类,第二个EventBus配置类要用到
import org.springframework.beans.BeansException;
import org.springframework.context.*;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtil.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> claz){
return ApplicationContextUtil.applicationContext.getBean(claz);
}
public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
return ApplicationContextUtil.applicationContext.getBeansOfType(clazz);
}
}
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.meituan.mtrace.thread.pool.ExecutorServiceTraceWrapper;
import xxx.utils.ApplicationContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Slf4j
@Configuration
@SuppressWarnings("all")
@DependsOn("applicationContextUtil")
public class EventBusConfiguration implements InitializingBean, DisposableBean {
// 异步event bus
private final static EventBus EVENT_BUS = new AsyncEventBus(
new ExecutorServiceTraceWrapper(new ThreadPoolExecutor(16, 32, 3L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(), new ThreadFactoryBuilder().setDaemon(false).setNameFormat("eventbus" +
"-thread-%d").build())
));
private Map<String, AbstractEventHandler> beans = null;
@Bean
public EventBus eventBus() {
return EVENT_BUS;
}
@Override
public void afterPropertiesSet() throws Exception {
// 把AbstractEventHandler和其子类都注册为订阅者
beans = ApplicationContextUtil.getBeansOfType(AbstractEventHandler.class);
if (beans != null) {
for (AbstractEventHandler eventAbstract : beans.values()) {
register(eventAbstract);
}
}
}
@Override
public void destroy() throws Exception {
if (beans != null) {
for (AbstractEventHandler eventAbstract : beans.values()) {
unregister(eventAbstract);
}
}
}
public static void register(AbstractEventHandler<? extends BaseEvent> handler) {
if (handler == null) {
return;
}
EVENT_BUS.register(handler);
log.info("Registered eventAdapter class: {}", handler.getClass());
}
public static void unregister(AbstractEventHandler<? extends BaseEvent> handler) {
if (handler == null) {
return;
}
EVENT_BUS.unregister(handler);
log.info("Unregisted eventAdapter class: {}", handler.getClass());
}
}
BaseEvent事件
所有要发布的事件,都实现BaseEvent,定义一个父类,主要用于可以在自己类中定义要传的属性值
public interface BaseEvent {
}
AbstractEventHandler抽象类
所有的订阅者都继承这个抽象类,然后重写处理方法
@Slf4j
public abstract class AbstractEventHandler<E extends BaseEvent> {
private static final String METHOD_NAME = "process";
@Subscribe
@SuppressWarnings("all")
public void onEvent(BaseEvent event) {
// 在类中找到具有所提供的名称和参数类型的方法
if (ReflectionUtils.findMethod(this.getClass(), METHOD_NAME, event.getClass()) != null) {
try {
if (!process((E) event)) {
log.error("handle {} fail, event: {}", event.getClass(), event);
}
} catch (Exception e) {
log.error("handle event {} exception, event: {}", event.getClass(), event, e);
}
}
}
/**
* 事件处理
* @param e event
* @return boolean
*/
public abstract boolean process(E e);
}
这么写,虽然每次新增的事件都要定义两个新类:EventBusEvent的新子类和AbstractEventHandler新订阅者处理逻辑,但解耦合
具体使用如下:
// 1.定义一个类,实现BaseEvent,定义要发布的事件具体属性字段
@Data
@ToString
@AllArgsConstructor
public class PeopleEvent implements BaseEvent {
private String name;
private int age;
private Boolean sex;
}
// 2.发布一个ImageBlackUrlEvent事件
// 这里ApplicationContextUtil.getBean(EventBus.class)获取到的就是配置类中的EventBus的bean
ApplicationContextUtil.getBean(EventBus.class).post(new PeopleEvent("张三",18,true));
// 3.写个PeopleEvent的订阅者,继承AbstractEventHandler类
@Slf4j
@Component
public class PeopleEventHandler extends AbstractEventHandler<PeopleEvent> {
@Override
public boolean process(PeopleEvent event) {
// TODO:具体的事件处理逻辑
return true;
}
}
也是第一次使用EventBus,可能有写的不对的地方,可以留言指出后续修改
404

被折叠的 条评论
为什么被折叠?



