• 在Spring中使用监听器以及事件监听机制


    1 事件监听概念

    应用程序事件允许我们发送和接收特定事件,我们可以根据需要处理这些事件。事件用于在松散耦合的组件之间交换信息。由于发布者和订阅者之间没有直接耦合,因此可以在不影响发布者的情况下修改订阅者,反之亦然。

    2 事件监听三要素

    1. 事件源:事件对象的产生者,任何一个事件都有一个来源
      ApplicationEvent
    2. 事件监听器:事件框架或组件收到一个事件后,需要通知所有相关的事件监听器来进行处理。这些监听器统一存储在事件监听器注册表中。
      ApplicationListener接口(编码式事件监听器)
      @EventListener(注解式事件监听器)
    3. 发布事件:ApplicationContext(spring容器)

    3 JDK事件与spring事件

    3.1 JDK事件机制

    在Java中,通过java.util. EventObject来描述事件,通过java.util. EventListener来描述事件监听器。

    // 用户实体
    public class User {
        private Long id;
        public Long getId() {return id;}
        public void setId(Long id) {this.id = id;}
        public User(Long id) {this.id = id;}
    }// 用户事件
    public class UserEvent extends EventObject {
        public UserEvent(Object source) {super(source);}
    }// 邮件事件
    public class EmailEvent extends UserEvent{
        public EmailEvent(Object source) {super(source);}
    }// 用户监听器
    public interface UserListener extends EventListener {
        // 处理用户注册事件
        void onRegister(UserEvent event);
    }// 邮件监听器
    public class EmailListener implements UserListener {
        @Override
        public void onRegister(UserEvent event) {
            if (event instanceof EmailEvent){
                User user = (User) event.getSource();
                System.out.println("给User Id为: " + user.getId() + "的用户发送邮件");
            }
        }
    }// 用户服务
    public class UserService {
        // 存放所有监听器
        private final List<UserListener> listeners = new ArrayList<>();
        // 添加监听器
        public void addListener(UserListener userListener){
            this.listeners.add(userListener);
        }
        // 注册用户并发布用户注册事件
        public void register(User user){
            System.out.println("注册新用户,Id为【" + user.getId() + "】");
            publishEvent(new EmailEvent(user));
        }
        // 广播用户注册事件
        private void publishEvent(UserEvent event){
            for (UserListener listener : listeners) {
                listener.onRegister(event);
            }
        }
        public static void main(String[] args) {
            UserService userService = new UserService();
            User user = new User(1000L);
            userService.addListener(new EmailListener());
            userService.register(user);
        }
    }// ==输出结果==
    // 注册新用户,Id为【1000】
    // 给User Id为: 1000的用户发送邮件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    3.2 Spring事件

    在Spring中,事件机制采用观察者模式进行具体实现,设计类图如下:

    1. 事件源:ApplicationEvent
    2. 事件监听器:ApplicationListener(编码式事件监听器)、@EventListener(注解式事件监听器)
    3. 发布事件:ApplicationContext(spring容器)

    ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。

    如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。这种事件机制都必须需要程序显示的触发。
    spring有一些内置的事件,当完成某种操作时会发出某些事件动作。比如监听ContextRefreshedEvent事件,当所有的bean都初始化完成并被成功装载后会触发该事件,实现ApplicationListener接口可以收到监听动作,然后可以写自己的逻辑。

    事件:ApplicationEvent

    ApplicationEvent是Spring事件的顶层抽象类,代表事件本身,继承自JDK的EventObject。Spring提供了一些内置事件,如ContextClosedEvent、ContextStartedEvent、ContextRefreshedEvent、ContextStopedEvent等

    事件类描述
    ContextRefreshedEventApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用
    ContextStartedEvent当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序
    ContextStoppedEvent当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作
    ContextClosedEvent当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
    RequestHandledEvent这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。

    事件监听器:ApplicationListener

    ApplicationListener是Spring事件监听器顶层接口,所有的监听器都实现该接口,继承自JDK的EventListener。Spring提供了一些易扩展的接口,如SmartApplicationListener、GenericApplicationListener

    事件发布:ApplicationEventPublisher

    ApplicationEventPublisher是Spring的事件发布接口,事件源通过该接口的publishEvent方法发布事件,所有的应用上下文都具备事件发布能力,因为ApplicationContext继承了该接口

    事件广播器:ApplicationEventMulticaster

    ApplicationEventMulticaster是Spring事件机制中的事件广播器,它默认提供一个SimpleApplicationEventMulticaster实现,如果用户没有自定义广播器,则使用默认的(初始化逻辑见AbstractApplicationContext的refresh方法)。它通过父类AbstractApplicationEventMulticaster的getApplicationListeners方法从事件注册表中获取事件监听器,并且通过invokeListener方法执行监听器的具体逻辑。
    默认的广播器是同步调用监听器的执行逻辑,但是可以通过为广播器配置Executor实现监听器的异步执行。

    在Spring中通常是ApplicationContext本身担任监听器注册表的角色,在其子类AbstractApplicationContext中就聚合了事件广播器ApplicationEventMulticaster和事件监听器ApplicationListnener,并且提供注册监听器的addApplicationListnener方法。

    当一个事件源产生事件时,它通过事件发布器ApplicationEventPublisher发布事件,然后事件广播器ApplicationEventMulticaster会去事件注册表ApplicationContext中找到事件监听器ApplicationListnener,并且逐个执行监听器的onApplicationEvent方法,从而完成事件监听器的逻辑。

    3.2 SpringBoot事件

    事件:SpringApplicationEvent

    SpringApplicationEvent是SpringBoot事件的顶层抽象类,且SpringBoot内置了7个事件:
    在这里插入图片描述

    • ApplicationStartingEvent:SpringBoot启动开始的时候发布的事件
    • ApplicationEnvironmentPreparedEvent:SpringBoot对应Environment准备完毕时发布的事件,此时上下文还没有创建,该事件中可以获取配置信息。通过监听该事件可以修改默认的配置信息,配置中心就是借助该事件完成将远端配置放入应用中
    • ApplicationContextInitializedEvent:当准备好上下文,并且在加载任何Bean之前发布的事件。该事件可以获取上下文信息
    • ApplicationPreparedEvent:上下文创建完成时发布的事件,此时的Bean还没有完全加载完成,只是加载了部分特定Bean。该事件可以获取上下文信息,但是无法获取自定义Bean,应为还没有被加载
    • ApplicationStartedEvent:上下文刷新(完成所有Bean的加载)之后,但是还没有调用任何ApplicationRunner 和CommandLineRunner运行程序之前发布的事件。该事件同样可以获取上下文信息,并且可以获取自定义的Bean
    • ApplicationReadyEvent:完成ApplicationRunner 和CommandLineRunner运行程序调用之后发布的事件,此时的SpringBoot已全部启动完成。该事件同样可以获取上下文信息
    • ApplicationFailedEvent:SpringBoot启动异常时发布的事件。该事件可以获取上下文信息和异常信息,通过该事件可以友好地完成资源的回收

    上述7个事件在容器启动的合适阶段进行发布,发布顺序自上而下,可查看SpringApplication的run(String… args)方法,分别对应SpringApplicationRunListener的7个方法,源码如下:

    // 该接口规定了SpringBoot的生命周期,会在各个生命周期广播响应的事件,调用实际的监听器
    public interface SpringApplicationRunListener {
        // 发布ApplicationStartingEvent
        default void starting() {
        }
        // 发布ApplicationEnvironmentPreparedEvent
        default void environmentPrepared(ConfigurableEnvironment environment) {
        }
        // 发布ApplicationContextInitializedEvent
        default void contextPrepared(ConfigurableApplicationContext context) {
        }
        // 发布ApplicationPreparedEvent
        default void contextLoaded(ConfigurableApplicationContext context) {
        }
        // 发布ApplicationStartedEvent
        default void started(ConfigurableApplicationContext context) {
        }
        // 发布ApplicationReadyEvent
        default void running(ConfigurableApplicationContext context) {
        }
        // 发布ApplicationFailedEvent
        default void failed(ConfigurableApplicationContext context, Throwable exception) {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    上述接口提供了唯一的实现类EventPublishingRunListener,该类聚合了SpringApplication和SimpleApplicationEventMulticaster,前4个事件的发布由广播器完成,后3个委托ApplicationContext完成发布,其实最终都是由广播器负责发布。

    3.4 自定义事件监听

    自定义事件监听一般包括事件源和、事件监听器

    3.4.1.自定义事件源(Event)

    import org.springframework.context.ApplicationEvent;
    
    public class TestEvent extends ApplicationEvent {
    
        public TestEvent(Object obj) {
            super(obj);
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.4.2.自定义事件监听器(Listener)

    1.自定义监听器类+@Component

    @Component
    public class TestListener implements ApplicationListener<TestEvent> {
        @Override
        public void onApplicationEvent(TestEvent event) {
    	    System.out.println("监听到事件发生:"+event.getClass());	
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.自定义监听器类+addListeners()

    public class TestListener implements ApplicationListener<TestEvent> {
        @Override
        public void onApplicationEvent(TestEvent event) {
    	    System.out.println("监听到事件发生:"+event.getClass());
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    @SpringBootApplication
    public class ScheduleApplication {
        public static void main(String[] args) {
            SpringApplication springApplication = new SpringApplication(ScheduleApplication.class);
            springApplication.addListeners(new TestListener());
            springApplication.run(args);
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.自定义监听器类+配置文件

    public class TestListener implements ApplicationListener<TestEvent> {
        @Override
        public void onApplicationEvent(TestEvent event) {
    	    System.out.println("监听到事件发生:"+event.getClass());
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    context:
      listener:
        classes: com.test.TestListener 
    
    • 1
    • 2
    • 3

    4.@EventListener+@Component

    @Component
    public class TestListener {
        @EventListener(value = TestEvent .class)
        public void eventListener(TestEvent event) {
            System.out.println("监听到事件发生:"+event.getClass());
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.4.3.发布事件测试

    @Component
    public class ScheduleApplication {
    
    	@Autowired
        private ApplicationContext context;
    
        public void publish() {
            context.publishEvent(new TestEvent(1));
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ApplicationListener接口和SmartApplicationListener接口区别

    • 实现ApplicationListener接口针对单一事件监听

    • 实现SmartApplicationListener接口针对多种事件监听

    SmartApplicationListener用法举例:

    @Component
    public class TestListener implements SmartApplicationListener {
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
            boolean b = TestEvent.class.isAssignableFrom(eventType) || Test2Event.class.isAssignableFrom(eventType);
            return b;
        }
    
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            System.out.println("监听到事件发生:"+event.getClass());
    
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 测试用例

      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.test.context.SpringBootTest;
      import org.springframework.context.ApplicationEventPublisher;
      import org.springframework.test.context.junit4.SpringRunner;
      
      @RunWith(SpringRunner.class)
      @SpringBootTest
      public class ListenerTest {
      
          @Autowired
          private ApplicationEventPublisher applicationEventPublisher;
      
          @Test
          public void test() {
              TestEvent testEvent = new TestEvent("hello word");
              applicationEventPublisher.publishEvent(testEvent);
          }
      } 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

    4 常用监听器接口

    ServletContextListener

    监听servletContext对象的创建以及销毁
    执行时机:

    1. contextInitialized(ServletContextEvent arg0) – 创建时执行

    2. contextDestroyed(ServletContextEvent arg0) – 销毁时执行

    ServletRequestListener

    监听request对象的创建以及销毁
    执行时机:

    1. requestInitialized(ServletRequestEvent sre) – 创建时执行

    2. requestDestroyed(ServletRequestEvent sre) – 销毁时执行

    HttpSessionListener

    监听session对象的创建以及销毁
    执行时机:

    1. sessionCreated(HttpSessionEvent se) – 创建时执行

    2. sessionDestroyed(HttpSessionEvent se) – 销毁时执行

    ServletContextAttributeListener

    监听servletContext对象中属性的改变
    执行时机:

    1. attributeAdded(ServletContextAttributeEvent event) – 添加属性时执行

    2. attributeReplaced(ServletContextAttributeEvent event) – 修改属性时执行

    3. attributeRemoved(ServletContextAttributeEvent event) – 删除属性时执行

    ServletRequestAttributeListener

    监听request对象中属性的改变

    1. attributeAdded(ServletRequestAttributeEvent srae) – 添加属性时执行

    2. attributeReplaced(ServletRequestAttributeEvent srae) – 修改属性时执行

    3. attributeRemoved(ServletRequestAttributeEvent srae) – 删除属性时执行

    HttpSessionAttributeListener

    监听session对象中属性的改变

    1. attributeAdded(HttpSessionBindingEvent event) – 添加属性时执行

    2. attributeReplaced(HttpSessionBindingEvent event) – 修改属性时执行

    3. attributeRemoved(HttpSessionBindingEvent event) – 删除属性时执行

    参考文章:
    https://blog.csdn.net/JokerLJG/article/details/121541262

    https://zhuanlan.zhihu.com/p/270596548

  • 相关阅读:
    Docker搭建RK3568开发环境
    SpringCloud整合Nacos
    2022年编程语言排名,官方数据来了,让人大开眼界
    什么是传递函数模型?
    Java之组合模式
    [附源码]Python计算机毕业设计Django路政管理信息系统
    Spring相关概念
    浏览器里玩机器学习、深度学习
    Mac 如何安装Redis
    2022祥云杯crypto部分
  • 原文地址:https://blog.csdn.net/weixin_43702146/article/details/127745430