eventBus发布订阅

背景说明

这两天有个需求,遇到个小问题:大项目A分为a、b、c、d几个小模块,假设a是Controller层,b是Serice层,c是对外提供的接口,b引了c的pom,c没有引b的pom,现在代码入口是c,然后需要调用b中的接口和方法,但因为c没有引b的pom,所以没办法注入Bean

  1. 简单的方法是c引入b的pom就可以,但尽量还是不要改代码模块之间的pom相互引入;
  2. c中声明Service,b中实现Service,然后在实现类中注入要调用b的接口bean,但这种又会导致文件混乱
  3. 通过MQ传递消息,但由于是一个项目中的数据,再接入MQ感觉更复杂了

然后就找找有没有其他什么方法,发现代码中有事件发布订阅功能,一测试还真能达到我想要的效果:c发布一个事件,然后在b中写事件订阅者,去调用b中的接口和方法去继续处理

由于之前对事件发布订阅没了解过,所以这里记录下

在Java中,EventBus 是一种用于在应用程序的不同部分之间通信的模式,它允许对象发布(发送)事件,而其他对象可以订阅(接收)这些事件。这种方式有助于解耦组件,提高代码的可维护性和可测试性。在Java中,有多种库和框架实现了EventBus模式,比如Google的Guava库中的EventBusSpring框架中的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,可能有写的不对的地方,可以留言指出后续修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值