SpringBoot 异步任务-Guava 中EventBus

目录

EventBus事件总线模式

pom

简单使用

创建消息接受类

测试类:

输出结果:

结论:

结合Spring使用

注入Bean

MyEventListener

消息基类

创建监听

发送消息

项目启动/测试

扩展

多线程

观察者模式和发布订阅模式的区别


  • git地址

EventBus事件总线模式

所谓Bus ,在计算机中就是存在主板上的总线,

在计算机上,我们的输入/输出设备种类繁多,当我们从键盘输入一个字符串, cpu 处理完成之后回显给显示器;这个过程的传输最直接的方式就是为这些设备相互之间建立线路,这样可以解决问题, 但是随着设备越来越多,私有的线路越来越繁杂,很难以维护,于是我们暂时丢弃了这个方式,采用公共的总线方式,所有的设备将 传输的数据,以及目的设备地址发送出去,总线只是为我们进行运输,这样也很方便维护

所以,软件中也借鉴了硬件中总线的背景,产生了事件总线模式,在软件中,组件模块会有很多,相互通信的协议,方式多种多样, 如果每个组件都要写一套,维护成本较高,那么我们就要用这个模式来解决这个问题.

这个模式跟发布-订阅模式,观察者模式都很类似。但相比之下,EventBus解除了通知者与观察者之间的关系,是组件之间能够进一步的解耦, 是设计模式中的观察者模式的优雅实现

传统上,Java的进程内事件分发都是通过发布者和订阅者之间的显式注册实现的。设计EventBus就是为了取代这种显示注册方式, 使组件间有了更好的解耦。使用上也非常简单

所涉及的类在eventbus 包下,主要包括:
EventBus: 事件总线,以及消息的广播传输
Subscriber: 订阅者,观察者
Dispatcher: 事件分发,有3种实现,分别是ImmediateDispatcher(同步分发器),LegacyAsyncDispatcher(异步分发器),PerThreadQueuedDispatcher(单线程分发)
SubscriberRegistry:订阅者注册器,通过ConcurrentMap, CopyOnWriteArraySet> 维护了事件消息类型和 观察者之间的关系;
SubscriberExceptionContext: 订阅者异常处理器上下文;
SubscriberExceptionHandler:异常处理器;
DeadEvent:死亡事件,事件没有相应的订阅者处理时,则标记为一个死亡事件;
AsyncEventBus:异步事件总线;

pom

            
            
                com.google.guava
                guava
                20.0
            

简单使用

创建消息接受类

这里多创建几种不同类型的Listener消息接受类(便于理解)

import com.google.common.eventbus.Subscribe;

/**
 * @author szw
 * @version 1.0
 * @since 2022/10/20 19:54
 */
public class EventListener1 {

    @Subscribe
    public void execute(String msg) {
        System.out.println("EventListener1 获得String消息" + msg);
    }
}

public class EventListener2 {

    @Subscribe
    public void execute(String msg){
        System.out.println("EventListener2 获得String消息" + msg);
    }
}

public class EventListener3 {

    @Subscribe
    public void execute(Integer msg) {
        System.out.println("EventListener3 获得Integer消息" + msg);
    }
}

public class EventListener4 {

    @Subscribe
    public void execute(EventEntity msg) {
        System.out.println("EventListener4 获得对象消息" + msg.toString());
    }
}

public class EventListener5 {

    @Subscribe
    public void execute(ChildrenEventEntity msg) {
        System.out.println("EventListener5 获得子类对象消息" + msg.toString());
    }
}



import java.io.Serializable;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * 消息封装类: 对象类型
 * @author szw
 * @version 1.0
 * @since 2022/10/21 09:33
 */
@Getter
@Setter
@ToString
public class EventEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer           id;

    private String            name;

    public EventEntity(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public EventEntity() {
    }
}


/**
 * 消息封装类: 对象类型, 测试子类
 * @author szw
 * @version 1.0
 * @since 2022/10/21 09:33
 */
@Getter
@Setter
@ToString
public class ChildrenEventEntity extends EventEntity {
    private static final long serialVersionUID = 1L;

    private String            name2;

    public ChildrenEventEntity(String name2) {
        this.name2 = name2;
    }
}

测试类:


import com.google.common.eventbus.EventBus;

/**
 * @author szw
 * @version 1.0
 * @since 2022/10/20 19:57
 */
public class Test {

    public static void main(String[] args) {
        // 实现事件总线
        EventBus eventBus = new EventBus();

        EventListener1 eventListener1 = new EventListener1();
        EventListener2 eventListener2 = new EventListener2();
        EventListener3 eventListener3 = new EventListener3();
        EventListener4 eventListener4 = new EventListener4();
        EventListener5 eventListener5 = new EventListener5();

        // 进行消息订阅
        eventBus.register(eventListener1);
        eventBus.register(eventListener2);
        eventBus.register(eventListener3);
        eventBus.register(eventListener4);
        eventBus.register(eventListener5);

        // 通知者发送消息
        eventBus.post("发送消息 啦啦啦.....");
        eventBus.post(1);
        eventBus.post(new EventEntity(100, "EventEntity父类对象消息"));
        eventBus.post(new ChildrenEventEntity("ChildrenEventEntity子类对象消息"));
    }
}

输出结果:

EventListener1 获得String消息发送消息 啦啦啦.....
EventListener2 获得String消息发送消息 啦啦啦.....
EventListener3 获得Integer消息1
EventListener4 获得对象消息EventEntity(id=100, name=EventEntity父类对象消息)
EventListener5 获得子类对象消息ChildrenEventEntity(name2=ChildrenEventEntity子类对象消息)
EventListener4 获得对象消息ChildrenEventEntity(name2=ChildrenEventEntity子类对象消息)

结论:

  1. eventBus会根据Listener的参数类型的不同,分别向不同的Subscribe发送不同的消息。
  2. 参数类型可以是封装类
  3. 参数类型相同的Listener会同时接到消息
  4. 关于继承,发送父类消息,子类不会接到父类的消息,发送子类消息, 子类和父类都可以收到

结合Spring使用

直接new的话也太不Spring了, 结合Spring使用的话还是用注入的方式, 这里简单实现下

注入Bean

@Configuration
@Slf4j
public class SpringConfig {

    @Bean("myAsyncEventBus")
    @Lazy(value = true)
    public AsyncEventBus createAsyncEventBus(ThreadPoolTaskExecutor threadPool) {
        log.info("myAsyncEventBus=============");
        return new AsyncEventBus(threadPool);
    }
}

MyEventListener

public abstract class MyEventListener implements InitializingBean {

    @Autowired
    private AsyncEventBus asyncEventBus;

    /**
     * 注册异步任务
     */
    @Override
    public void afterPropertiesSet() {
        asyncEventBus.register(this);
    }
}

消息基类

public class TestEvent {

}

创建监听

@Component
public class TestEventListener extends MyEventListener {

    @Subscribe
    @AllowConcurrentEvents
    public void execute(TestEvent event) {
        System.out.println("收到消息");
    }
}

发送消息

    @Autowired
    private AsyncEventBus asyncEventBus;

    @ResponseBody
    @RequestMapping("/test")
    public void test() {
        asyncEventBus.post(new TestEvent());
    }

项目启动/测试

SpringBoot 异步任务-Guava 中EventBus_第1张图片

扩展

多线程

EventBus默认是线程安全的, 内部SynchronizedSubscriber在调用业务逻辑的时候,会使用synchronized块加锁,给了多线程但实际依旧是多线程抢占锁然后顺序执行

如要使用多线程需要在订阅@Subscribe处加上@AllowConcurrentEvents注解, 注意加入此注解需要业务自己保证线程安全

    @Subscribe
    @AllowConcurrentEvents
    public void execute(Test1Event event) {

观察者模式和发布订阅模式的区别

两种模式都可以用于松散耦合,改进代码管理和潜在的复用。

  • 最大的区别是调度的地方:  观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方来实现调度的,发布者和订阅者之间互不感知
  • 从表面上看: 观察者模式里,只有两个角色一一 观察者+被观察者, 而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个经常被我们忽略的一一经纪人Broker
  • 往更深层次讲: 观察者和被观察者,是松耦合的关系, 发布者和订阅者,则完全不存在耦合
  • 从使用层面上讲: 观察者模式,多用于单个应用内部, 发布订阅模式,则更多的是一种跨应用的模式(cross-application pattern),比如消息中间件

你可能感兴趣的:(Java,java技术,spring,boot,后端,java,Guava,EventBus)