Springboot事件机制整合EventBus应用(事件驱动模型)

7 篇文章 0 订阅
3 篇文章 1 订阅

事件监听机制

熟悉Spring的同学,spring提供了一套完整的事件监听机制。要了解spring不妨先熟悉一下,观察者模式。Java 从1.0版本,已经有了观察者模式的设计。下面通过一个案例来了解一下Java所提供的观察者模式。

观察者模式

观察者模式为,一个被观察者Observable(被观察主题)和多个观察者Observer,被观察者中存储了所有的观察者对象,当被观察者接收到一个外界的消息,会通知其所有的观察者。其实就是一个主题改变,订阅它的观察者都会收到相关的消息。(也可以叫做,发布订阅模式)
JDK 观察者模式
JDK 以上是观察者模式的类图。 观察者(Obsever)、被观察者(Observable)。通过源码可以看出被观察者中会维护观察者对象,观察者注册到被观察者列表后,被观察者发出的通知观察者都将收到改变。而观察者中的unpdate(Observable,Object)方法。就是通过监听被观察者发生的改变,来触发观察者的响应。
模拟老师和学生的一个场景:被观察者(老师)、观察者(学生)。

案例1.

观察者

public class MyObserver implements Observer {
    /**
     * 观察者(学生)name
     */
    private String name;
    public MyObserver(Observable o, String name) {
        o.addObserver(this);
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("观察者(学生)" + name + "收到作业!《" + arg + "》"+"目标的观察者数量=" + o.countObservers());
    }
}

被观察者

public class MyObserverable extends Observable {
    //被观察者数据
    private String data;
    public String getData() {
        return data;
    }
    /**
     * 如果有如果改变
     * @param data
     */
    public void setData(String data) {
         //更改变化状态
         setChanged();
        //通知注册的观察者
        notifyObservers(data);
    }
}

测试

public static void main(String[] args) {
        //1.构造被观察目标。(假如现实场景中的老师)
        MyObserverable observerable = new MyObserverable();
        //2.构造2个观察者实现类:添加观察者进观察目标  (现实场景中的学生,每来一个新学生,要加入老师的观察者名录中)
        MyObserver observer1 = new MyObserver(observerable, "tom");
        MyObserver observer2 = new MyObserver(observerable, "jerry");
        //3.被观察者(老师)发布今天的作业任务。其注册的观察者们(学生们)响应。
        observerable.setData("Java从入门到放弃");
    }

结果:

被观察者已上线....
观察者(学生)jerry收到作业!《Java从入门到放弃》目标的观察者数量=2
观察者(学生)tom收到作业!《Java从入门到放弃》目标的观察者数量=2

通过代码看到,被观察者的 通知注册的观察者 notifyObservers(data);,触发消息的通知给观察者们。观测者观察到有变化后,做出改变。update(Observable o, Object arg) {}.

Spring事件机制(事件监听机制)

通过上面的观察者模式,我们能够了解到,其中的设计模式及思想。监听者模式有异曲同工之处,监听者模式包含了一个监听者Listener与之对应的事件Event,还有一个事件发布者EventPublish,过程就是EventPublish发布一个事件,被监听者捕获到,然后做出相应的处理。事件监听机制,其实从JDK 1.1开始有的设计模式,其主要的几个基类为 事件源EventObject 、监听者EventListener、发布者(Spring)ApplicationEventPublisher
spring事件驱动模型
下面通过案例演示事件发布订阅。

案例2.

事件源:
Spring的事件源为ApplicationEvent,继承至JDK提供的EventObject 基类。

public class MyContextEvent extends ApplicationEvent {
    public MyContextEvent(Object source) {
        super(source);
        System.out.println("source message->"+source.toString());
    }
}

监听者:
Spring的监听者为ApplicationListener,继承至JDK提供的EventListener 接口。其实EventListener中没有任何方法定义,只是作为监听者标识。

public class MyContextListener implements ApplicationListener<MyContextEvent> {
    @Override
    public void onApplicationEvent(MyContextEvent myContextEvent) {
        System.out.println("listener this MyContextEvent....");
    }
}

这里我们通过Spring容器的事件发布功能来实现,自动以事件的注册发布及监听。
在spring容器事件中AbstractApplicationContext继承至ConfigurableApplicationContext,
ConfigurableApplicationContext类继承至ApplicationContext。IOC容器的核心接口ApplicationContext中继承了,事件发布ApplicationEventPublisher. 其子类AbstractApplicationContext中实现了父接口ApplicationEventPublisher中的publishEvent(ApplicationEvent event)方法。

//AbstractApplicationContext类中的publishEvent方法。
public void publishEvent(ApplicationEvent event) {
        this.publishEvent(event, (ResolvableType)null);
    }

测试

public static void main(String[] args) {
        //获取IOC容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注册监听者
        context.register(MyContextListener.class);
        //刷新容器
        context.refresh();
        //事件发布
        context.publishEvent(new MyContextEvent("publish this event ...."));
    }

结果

source message->publish this event ....
listener this MyContextEvent....

在IOC容器中,添自定义事件的监听者context.register(MyContextListener.class);
事件发布通过发布自定义事件== context.publishEvent(new MyContextEvent(“publish this event …”));==自定义消息。监听者做出响应。

基于Springboot事件监听机制整合EventBus应用

前面将观察者模式以及spring的事件发布机制。通过案例代码的方式,介绍了一下。下面介绍一下基于观察者模式的一个落地实现,Google Guava的EventBus事件处理。EventBus无需实现复杂的事件、监听者、发布者。只需要通过提供的API来实现事件机制。Evnet是根据发布者发布的事件类型,监听者订阅此事件类型,来时间发布订阅模式。在使用EventBus时,我们只需将自己要发布的事件类型通过EventBus API中的public void post(Object event)方法。监听者通过@Subscribe监听指定类型的事件。
springboot事件机制也是基于spring事件机制实现。spring boot中支持的事件类型定在org.springframework.boot.context.event包中,springboot的项目启动加载过程,是通过springboot事件触发器EventPublishingRunListener来完成。目前支持的事件类型有如下6种。

  1. ApplicationStartedEvent spring boot 启动监听类
  2. ApplicationEnvironmentPreparedEvent 环境事先准备,spring boot中的环境已经准备ok
  3. ApplicationPreparedEvent 上下文准备事件
  4. ApplicationReadyEvent 上下文已经准备ok
  5. ApplicationFailedEvent 该事件为spring boot启动失败时的操作(启动时出现异常事件)
  6. SpringApplicationEvent 获取SpringApplication
    每一步加载过程通过触发不同的事件,来完成相应的操作。这里不一一赘述,springboot的事件。感兴趣的同学可以通过翻看启动类SpringApplication.run(CommonApplication.class, args)SpringApplication类中的run方法来研究整个启动过程,做了哪些工作,网上的这部分的讲解也是一大堆,贴一张springboot的启动流程。
    springboot启动流程
    EventBus API的调用实现起来并不复杂,下面通过一个实际项目中的运用,结合springboot的事件发布机制,来整合基于事件驱动的项目案例。首先要清楚前面所将的Spring的事件发布机制,通过Spring的事件发布机制,整合EventBus。

思想思路:
1.通过Springboot的事件加载机制,监听ApplicationPreparedEvent上下文准备事件,在上下文准备事件时,将监听这一事件的监听者(实现spring中的ApplicationListener),添加至EventBus的事件监听者中(即 Event的监听者注册register(Object handler))。
2.添加订阅者(即实现ApplicationListener监听者)的监听者。通过注解@Subscrib实现监听。
3.事件发布。调用EventBus中的事件发布API post(Object event).发布事件。
4.订阅者响应。

案例3.

Maven 引用相应Jar包
EventBus 事件总线

//api封装
public class MyEventBus {

    /** 事件任务总线 */
    private final static EventBus tiemEventBus = new EventBus();
    /**
     * 触发同步事件
     *
     * @param event
     */
    public static void post(Object event) {
        tiemEventBus.post(event);
    }
    /**
     * 注册事件处理器
     *
     * @param handler
     */
    public static void register(Object handler) {
        tiemEventBus.register(handler);
    }
    /**
     * 注销事件处理器
     *
     * @param handler
     */
    public static void unregister(Object handler) {
        tiemEventBus.unregister(handler);
    }
}

消息实体类

public class Message {
    private MessageType messageType;
    private String messageContent;

    public Message(MessageType messageType, String messageContent) {
        this.messageType = messageType;
        this.messageContent = messageContent;
    }
    public MessageType getMessageType() {
        return messageType;
    }
    public void setMessageType(MessageType messageType) {
        this.messageType = messageType;
    }
    public String getMessageContent() {
        return messageContent;
    }
    public void setMessageContent(String messageContent) {
        this.messageContent = messageContent;
    }

    public enum MessageType {
        OPENDOOR(1, "openDoor"),
        CLOSEDOOR(2,"closeDoor");
        private int code;
        private String value;

        MessageType(int code, String value) {
            this.code = code;
            this.value = value;
        }
        public int getCode() {
            return code;
        }
        public void setCode(int code) {
            this.code = code;
        }
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
        }
}

事件监听者
监听ApplicationPreparedEvent上下文准备事件,即springboot加载至这一步时,将此监听者注册到EventBus中。通过抽象封装的方式,后继承的子类通过实现此类,实现订阅者的eventBus注册。

@Component
abstract class MyApplicationListener implements ApplicationListener<ApplicationPreparedEvent> {
    /**
     *  ApplicationPreparedEvent 上下文准备事件
     * @param applicationPreparedEvent
     */
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) {
        ConfigurableApplicationContext applicationContext = applicationPreparedEvent.getApplicationContext();
        MyApplicationListener bean = applicationContext.getBean(this.getClass());
        System.out.println("regist listener to eventBus...."+bean);
        MyEventBus.register(bean);
    }
}

订阅者(也即监听者)继承至MyApplicationListener。

@Component
public class MyListentenerSubscribe extends MyApplicationListener{
    @Subscribe
    public void on(Message message){
        System.out.println("subscribe message->  messgeType:"+message.getMessageType()+"\n messageContent:"+message.getMessageContent());
    }
}

测试
这里因为是案例实现,直接在Controller实现时间的发布。真实的业务中,通业务层,具体业务触发的事件的发布。
我通过发布User

@RestController
public class EventPublishCtrl extends LogBase {
    @GetMapping("/publish")
    public void publishEvent() {
        log.info("this publish method...");
        MyEventBus.post(new Message(Message.MessageType.OPENDOOR,"芝麻开门!"));
    }
}

启动输出

2019-08-22 14:39:15.811 WARN 73026 — [ main] com.netflix.discovery.DiscoveryClient : Using default backup registry implementation which does not do anything.
2019-08-22 14:39:15.813 INFO 73026 — [ main] com.netflix.discovery.DiscoveryClient : Starting heartbeat executor: renew interval is: 30
2019-08-22 14:39:15.814 INFO 73026 — [ main] c.n.discovery.InstanceInfoReplicator : InstanceInfoReplicator onDemand update allowed rate per min is 4
2019-08-22 14:39:15.818 INFO 73026 — [ main] com.netflix.discovery.DiscoveryClient : Discovery Client initialized at timestamp 1566455955817 with initial instances count: 0
2019-08-22 14:39:15.820 INFO 73026 — [ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application UNKNOWN with eureka with status UP
2019-08-22 14:39:15.820 INFO 73026 — [ main] com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1566455955820, current=UP, previous=STARTING]
2019-08-22 14:39:15.822 INFO 73026 — [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_UNKNOWN/kafka1:9090: registering service…
regist listener to eventBus....com.uniubi.springcloud.practice.eventBus.MyListentenerSubscribe@329a1f8d
2019-08-22 14:39:15.914 INFO 73026 — [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9090 (http) with context path ‘’
2019-08-22 14:39:15.915 INFO 73026 — [ main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 9090
2019-08-22 14:39:15.917 INFO 73026 — [ main] c.u.s.practice.PracticeApplication : Started PracticeApplication in 4.101 seconds (JVM running for 4.895)
2019-08-22 14:39:16.761 INFO 73026 — [-192.168.63.121] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet ‘dispatcherServlet’

项目的启动加载,注册相应的监听者至eventBus中。
测试事件发布

2019-08-22 14:43:44.399  INFO 73026 --- [nio-9090-exec-1] c.u.springcloud.practice.common.LogBase  : this publish method...
subscribe message->  messgeType:OPENDOOR
 messageContent:芝麻开门!

订阅者收到具体的消息类型,以及消息内容。
总结

springBoot的底层实现,很多都是基于事件的发布订阅模式来做的。我们日常的开发过程中,做到业务的解耦,消息的异步传输。都可以通过实现发布订阅的模式来实现。这里也是实现项目中基于事件驱动,来完成核心业务的解耦。

  • 文中的图片来自网络。如有侵权行为联系作者。
  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Spring Boot中集成EventBus可以通过以下步骤进行: 1. 添加EventBus依赖:在`pom.xml`文件中添加EventBus的依赖,例如: ```xml <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency> ``` 2. 创建事件类:创建表示事件的类,例如: ```java public class MyEvent { private String message; public MyEvent(String message) { this.message = message; } public String getMessage() { return message; } } ``` 3. 创建事件监听器:创建事件监听器类,用于处理事件,例如: ```java @Component public class MyEventListener { @Subscribe public void handleEvent(MyEvent event) { // 处理事件逻辑 System.out.println("Received event: " + event.getMessage()); } } ``` 4. 配置EventBus:在Spring Boot的配置类中配置EventBus,例如: ```java @Configuration public class EventBusConfig { @Bean public EventBus eventBus() { return new EventBus(); } @Autowired public void setListeners(EventBus eventBus, MyEventListener listener) { eventBus.register(listener); } } ``` 5. 发布事件:在需要发布事件的地方,注入EventBus并发布事件,例如: ```java @Service public class MyService { @Autowired private EventBus eventBus; public void doSomething() { // 执行业务逻辑 // ... // 发布事件 eventBus.post(new MyEvent("Hello EventBus!")); } } ``` 这样,当`MyService`中的`doSomething`方法被调用时,会触发`MyEvent`事件,并由`MyEventListener`监听器处理该事件
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值