SpringBoot--事件监听机制--使用实例原理

SpringBoot--事件监听机制--使用实例原理,第1张

SpringBoot--事件监听机制--使用/实例/原理

原文网址:SpringBoot--事件监听机制--使用/实例/原理_IT利刃出鞘的博客-CSDN博客

简介

说明

        本文用示例介绍SpringBoot中的事件的用法及原理。

        SpringBoot的启动过程用到了事件监听机制,它属于观察者模式。学习事件监听机制可以让我们更好地理解SpringBoot的启动流程,也有利于将观察者模式应用于项目。

事件监听简述

        事件的发布与监听从属于观察者模式;和MQ相比,事件的发布与监听偏向于处理“体系内”的某些逻辑。 

        多个监听器可以监听同一个事件。例如:发布了事件A,监听器A和监听器B都监听了事件A,则监听器A和B都会进行处理。

同步与异步监听

监听方式特点使用时机同步监听发布器线程与监听器线程处于同一线程

1.监听逻辑处理较快

2.需要紧接着根据监听器追踪业务线程

异步监听发布器线程与监听器线程处于不同线程

1.监听逻辑处理比较耗时

2.追求响应性能

优先级

可使用实现Ordered接口的方式,调整监听器顺序。

注意:必须是同时实现 ApplicationListener,Ordered这样的方法才能控制顺序。

下边几种都是无法控制顺序的:

  1. @Component+@EventListerner+实现Ordered
  2. 实现 ApplicationListener+@Order
实例

同步监听(无序)

事件

package com.example.event;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    public MyEvent(Object source) {
        super(source);
    }

}

监听器

监听器1

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener {
    @EventListener
    public void abc(MyEvent event) {
        System.out.println("监听器:        " + "MyListener");
        System.out.println("监听器所在线程:" + Thread.currentThread().getName());
        System.out.println("事件:          " + event);
        System.out.println("事件的数据:    " + event.getSource());
    }
}

 监听器2

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener2 {

    @EventListener
    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:      " + "MyListener2");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

发布器

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        System.out.println("发布器所在线程:" + Thread.currentThread().getName());
        applicationContext.publishEvent(new MyEvent(message));
    }
}

测试

写一个Controller

package com.example.controller;

import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyPublisher myPublisher;

    @GetMapping("/test1")
    public String test1() {
        myPublisher.myPublish("Hello");
        return "test1 success";
    }
}

启动后,访问:http://localhost:8080/test1

后端输出:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello

可以发现,所有监听器和发布器都在同一个线程。 

同步监听(有序)

事件

package com.example.event;
 
import org.springframework.context.ApplicationEvent;
 
public class MyEvent extends ApplicationEvent {
 
    public MyEvent(Object source) {
        super(source);
    }
 
}

监听器

监听器1

package com.example.event;

import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Component
public class MyListener implements ApplicationListener,  Ordered {
    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }

    @Override
    public int getOrder() {
        return 2;
    }
}

监听器2

package com.example.event;

import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Component
public class MyListener2 implements ApplicationListener, Ordered {

    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:      " + "MyListener2");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

发布器

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        System.out.println("发布器所在线程:" + Thread.currentThread().getName());
        applicationContext.publishEvent(new MyEvent(message));
    }
}

测试

写一个Controller

package com.example.controller;

import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyPublisher myPublisher;

    @GetMapping("/test1")
    public String test1() {
        myPublisher.myPublish("Hello");
        return "test1 success";
    }
}

启动后,访问:http://localhost:8080/test1

后端输出:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello

如果将监听器的实现的Ordered顺序颠倒,则输出结果如下:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
异步监听(无序)

方法:

  1. 开启异步监听
  2. 在监听器上加@Async(此监听器必须是@Component方法注册的)

事件

package com.example.event;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    public MyEvent(Object source) {
        super(source);
    }

}

同步监听器

package com.example.event;
 
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
 
@Component
public class MyListener {
    @EventListener
    public void abc(MyEvent event) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

异步监听器

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@Async
public class MyListener2 {

    @EventListener
    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:      " + "MyListener2");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

发布器

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        System.out.println("发布器所在线程:" + Thread.currentThread().getName());
        applicationContext.publishEvent(new MyEvent(message));
    }
}

启动类

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

测试

写一个Controller

package com.example.controller;

import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyPublisher myPublisher;

    @GetMapping("/test1")
    public String test1() {
        myPublisher.myPublish("Hello");
        return "test1 success";
    }
}

启动后,访问:http://localhost:8080/test1

后端输出:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
监听器:      MyListener2
  所在线程:  task-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
流程 自定义事件

通过继承ApplicationEvent来自定义事件。

构造器的参数为该事件的相关数据对象,监听器可以获取到该数据对象,进而进行相关逻辑处理。

package com.example.event;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    public MyEvent(Object source) {
        super(source);
    }

}
自定义监听器 ApplicationListener

法1:@EventListener

监听单个事件

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

public class MyListener {
    @EventListener
    public void abc(MyEvent event) {
        System.out.println("监听器:     " + "MyListener");
        System.out.println("  线程:     " + Thread.currentThread().getName());
        System.out.println("  事件:     " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

或者

package com.example.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

public class MyListener {
    @EventListener({MyEvent.class})
    public void abc(ApplicationEventevent) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  线程:      " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据: " + event.getSource());
    }
}

 上边的办法比较好,因为不需要类型转换了。直接就能确定是MyEvent类型。

监听多个事件

事件进来之后,可以使用if(event instanceOf MyEvent.class)来判断是哪种事件。

package com.example.event;
 
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
 
@Component
public class MyListener {
    @EventListener({MyEvent.class, MyEvent2.class})
    public void abc(ApplicationEvent event) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

监听所有ApplicationEvent

若使用这种写法,启动时会打印很多Spring自带的事件。任意ApplicationEvent都会进入这里边。

package com.example.event;
 
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
 
@Component
public class MyListener {
    @EventListener
    public void abc(ApplicationEvent event) {
        System.out.println("监听器:     " + "MyListener");
        System.out.println("  所在线程: " + Thread.currentThread().getName());
        System.out.println("  事件:     " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

法2:实现ApplicationListener接口

public class MyListener implements ApplicationListener{

	public void onApplicationEvent(MyEvent event){
        System.out.println("监听器:     " + "MyListener");
        System.out.println("  所在线程: " + Thread.currentThread().getName());
        System.out.println("  事件:     " + event);
        System.out.println("  事件的数据:" + event.getSource());
	}
}
SmartApplicationListener

源码如下

public interface SmartApplicationListener extends ApplicationListener, Ordered {
    boolean supportsEventType(Class var1);

    default boolean supportsSourceType(@Nullable Class sourceType) {
        return true;
    }

    default int getOrder() {
        return 2147483647;
    }
}

supportsEventType:支持的事件的类型

supportsSourceType:支持的数据的类型

getOrder:2147483641:是2^31-1,也就是32位的int的最大正数 。

示例

事件

package com.example.event;
 
import org.springframework.context.ApplicationEvent;
 
public class MyEvent extends ApplicationEvent {
 
    public MyEvent(Object source) {
        super(source);
    }
 
}

监听器1

package com.example.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class aClass) {
        return aClass == MyEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class sourceType) {
        return sourceType == String.class;
    }

    @Override
    public int getOrder() {
        return 2;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
        System.out.println("  是MyEvent?:" + (event instanceof MyEvent));
    }
}

监听器2

package com.example.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener2 implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class aClass) {
        return aClass == MyEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class sourceType) {
        return sourceType == String.class;
    }

    @Override
    public int getOrder() {
        return 1;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("监听器:      " + "MyListener2");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
        System.out.println("  是MyEvent?:" + (event instanceof MyEvent));
    }
}

发布器

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        System.out.println("发布器所在线程:" + Thread.currentThread().getName());
        applicationContext.publishEvent(new MyEvent(message));
    }
}

测试

package com.example.controller;

import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyPublisher myPublisher;

    @GetMapping("/test1")
    public String test1() {
        myPublisher.myPublish("Hello");
        return "test1 success";
    }

}

启动后,访问:http://localhost:8080/test1

后端输出:

发布器所在线程:http-nio-8080-exec-2
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-2
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
  是MyEvent?:true
监听器:      MyListener
  所在线程:  http-nio-8080-exec-2
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
  是MyEvent?:true

如果将监听器的实现的Ordered顺序颠倒,则输出结果如下:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
  是MyEvent?:true
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
  是MyEvent?:true
注册监听器 方式适用范围能否搭配@Async注解,进行异步监听@Component所有监听器能application.yml中添加配置实现ApplicationListener接口的监听器不能启动类中注册实现ApplicationListener接口的监听器不能

法1:@Component(适用于所有监听器)

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener {
    @EventListener
    public void abc(MyEvent event) {
        System.out.println("监听器:" + "MyListener");
        System.out.println("线程:" + Thread.currentThread().getName());
        System.out.println("事件:" + event);
        System.out.println("事件的数据:" + event.getSource());
    }
}
package com.example.event;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener2 implements ApplicationListener {

    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:" + "MyListener2");
        System.out.println("线程:" + Thread.currentThread().getName());
        System.out.println("事件:" + event);
        System.out.println("事件的数据:" + event.getSource());
    }
}

法2:application.yml中添加配置(只适用于实现ApplicationListener接口的监听器)

context:
  listener:
    classes: com.example.event.MyListener,com.example.event.MyListener2

法3:启动类中注册(只适用于实现ApplicationListener接口的监听器)

package com.example;

import com.example.event.MyListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        // 原来是这样的:SpringApplication.run(DemoApplication.class, args);
        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        context.addApplicationListener(new MyListener());
    }
}
发布事件

法1:注入ApplicationContext,调用其publishEvent方法

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        applicationContext.publishEvent(new MyEvent(message));
    }
}

法2:启动类中发布

package com.example;

import com.example.event.MyEvent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        //原来是:SpringApplication.run(DemoApplication.class, args);
        ConfigurableApplicationContext context =SpringApplication.run(DemoApplication.class, args);
        context.publishEvent(new MyEvent("Hello"));
    }
}
SpringBoot启动事件

其他网址

Spring Boot 启动事件和监听器,太强大了!_Java技术栈,分享最主流的Java技术-CSDN博客

启动事件(按先后顺序)

  1. ApplicationStartingEvent
    1. 这个事件在 Spring Boot 应用运行开始时,且进行任何处理之前发送(除了监听器和初始化器注册之外)。
  2. ApplicationEnvironmentPreparedEvent
    1. 这个事件在当已知要在上下文中使用 Spring 环境(Environment)时,在 Spring 上下文(context)创建之前发送。
  3. ApplicationContextInitializedEvent
    1. 这个事件在当 Spring 应用上下文(ApplicationContext)准备好了,并且应用初始化器(ApplicationContextInitializers)已经被调用,在 bean 的定义(bean definitions)被加载之前发送。
  4. ApplicationPreparedEvent
    1. 这个事件是在 Spring 上下文(context)刷新之前,且在 bean 的定义(bean definitions)被加载之后发送。
  5. ApplicationStartedEvent
    1. 这个事件是在 Spring 上下文(context)刷新之后,且在 application/ command-line runners 被调用之前发送。
  6. AvailabilityChangeEvent
    1. 这个事件紧随上个事件之后发送,状态:ReadinessState.CORRECT,表示应用已处于活动状态。
  7. ApplicationReadyEvent
    1. 这个事件在任何 application/ command-line runners 调用之后发送。
  8. AvailabilityChangeEvent
    1. 这个事件紧随上个事件之后发送,状态:ReadinessState.ACCEPTING_TRAFFIC,表示应用可以开始准备接收请求了。
  9. ApplicationFailedEvent
    1. 这个事件在应用启动异常时进行发送。

        上面所介绍的这些事件列表仅包括绑定到 SpringApplication 的 SpringApplicationEvents 事件,除了这些事件以外,以下事件也会在 ApplicationPreparedEvent 之后和 ApplicationStartedEvent 之前发送:

  1. WebServerInitializedEvent
    1. 这个 Web 服务器初始化事件在 WebServer 启动之后发送,对应的还有 ServletWebServerInitializedEvent(Servlet Web 服务器初始化事件)、ReactiveWebServerInitializedEvent(响应式 Web 服务器初始化事件)。
  2. ContextRefreshedEvent
    1. 这个上下文刷新事件是在 Spring 应用上下文(ApplicationContext)刷新之后发送。 
原理

源码分析

myPublisher.myPublish("Hello") //HelloController
    applicationContext.publishEvent(new MyEvent(message)); //MyPublisher
        publishEvent(event, (ResolvableType)null); //AbstractApplicationContext.class
            // AbstractApplicationContext
            getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
                // AbstractApplicationContext
                applicationEventMulticaster.multicastEvent((ApplicationEvent)applicationEvent, eventType) 
                    
SimpleApplicationEventMulticaster#multicastEvent
以下都在SimpleApplicationEventMulticaster
multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType)
    // 此时,type为:com.example.tmp.MyEvent
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        for (ApplicationListener listener : getApplicationListeners(event, type))
            invokeListener(listener, event)
                
                
AbstractApplicationEventMulticaster#getApplicationListeners
getApplicationListeners(event, type))
    retriever = new ListenerRetriever(true);
    Collection> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);
        listeners = new linkedHashSet<>(this.defaultRetriever.applicationListeners);
            遍历listeners,如果监听器支持此事件,则加入集合并返回。

其他网址

事件监听(基于SpringBoot示例)_JustryDeng-CSDN博客

深入浅出Spring/SpringBoot 事件监听机制 - 知乎
Springboot事件机制整合EventBus应用(事件驱动模型)_fw19940314的博客-CSDN博客

SpringBoot事件监听_Chavaer-CSDN博客
SpringBoot-事件监听的4种实现方式_ignorewho的博客-CSDN博客_springboot事件监听

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5434006.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存