spring-event-事件监听机制实现

1. 事件监听机制概述

1. 场景

模型版本更新了,新版本需要继承老版本的构件分享、自定义属性、着色数据,以后还可能有其他数据要继承,这些数据之间没有直接联系,就是当模型版本变更的时候,他们各自需要执行。

2. 涉及的三个对象

  • 事件源(提供事件处理时的元数据)

这里就是模型版本更新了

  • 监听器(事件处理者)

构件分享继承是一个监听者,监听到版本更新的事件,需要自己处理点东西
自定义属性继承也是一个监听者。。
着色数据继承也是一个监听者。。。

  • 事件发布者(调用者)

2. 示例

1. 事件源 ApplicationEvent

定义一个事件源,只需要继承 org.springframework.context.ApplicationEvent.ApplicationEvent 或其子类

package com.demo.event;

import org.springframework.context.ApplicationEvent;

/**
 * 事件源:模型变更了
 * 伪代码:模型变更
 * */
public class ModelExchangeEvent extends ApplicationEvent {

    private Integer modelId;
    private Integer modelVersion;

    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public ModelExchangeEvent(Object source, Integer modelId, Integer modelVersion) {
        super(source);
        this.modelId = modelId;
        this.modelVersion = modelVersion;
    }

    public Integer getModelId() {
        return modelId;
    }
    public Integer getModelVersion() {
        return modelVersion;
    }
    public void setModelId(Integer modelId) {
        this.modelId = modelId;
    }
    public void setModelVersion(Integer modelVersion) {
        this.modelVersion = modelVersion;
    }
}

2. 监听者 ApplicationListener

监听器可以有多个,每个监听器代表一种操作。

  • 通过继承 ApplicationListener 实现监听器
package com.demo.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 伪代码:着色继承
 * 实现 `ApplicationListener` 方式
 * */
@Component
@Slf4j
public class ModelExchangeColorListener implements ApplicationListener<ModelExchangeEvent> {
    @Override
    public void onApplicationEvent(ModelExchangeEvent e) {
        log.info("收到模型{}版本{}的变更通知!着色继承开始!", e.getModelId(), e.getModelVersion());
        //模拟逻辑运行
        try {
            Thread.sleep(2_000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        log.info("着色继承结束!");
    }
}
  • 通过 @EventListener 注解实现监听器
package com.demo.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 伪代码:继承
 * 使用 @EventListener 方式
 * */
@Component
@Slf4j
public class ModelExchangePropertiesListener {

    /**
     * 属性继承
     * */
    @EventListener(ModelExchangeEvent.class)
    public void onEvent(ModelExchangeEvent e){
        log.info("收到模型{}版本{}的变更通知!自定义属性继承开始!", e.getModelId(), e.getModelVersion());
        //模拟逻辑运行
        try {
            Thread.sleep(2_000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        log.info("自定义属性继承结束!");
    }

    /**
     * 分享继承
     * */
    @EventListener(ModelExchangeEvent.class)
    public void sss(ModelExchangeEvent e){
        log.info("收到模型{}版本{}的变更通知!分享继承开始!", e.getModelId(), e.getModelVersion());
        //模拟逻辑运行
        try {
            Thread.sleep(2_000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        log.info("分享继承结束!");
    }
}

3. 事件发布者 ApplicationEventPublisher

实现了 ApplicationEventPublisher 接口,就是一个发布者,通过 publishEvent 方法发布事件。

  • 直接注入 ApplicationEventPublisher 对象
package com.demo.controller;

import com.demo.event.ModelExchangeEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/model")
@Slf4j
public class ModelController{

    /**
     * 使用 ApplicationEventPublisher 发布事件
     * */
    @Autowired
    private ApplicationEventPublisher ap;

    @RequestMapping("/exchange")
    public void exchange(){
        log.info("伪代码:模型变更的逻辑");
        ap.publishEvent(new ModelExchangeEvent(ap, 9001, 2));
    }
}

这里通过自动注入一个 ApplicationEventPublisher 来发布事件,这个注入的 publisher 其实就是启动的 Spring 容器对象 ConfigurableApplicationContext run = SpringApplication.run(EventMain.class, args),也就是这个 run

  • 通过 ApplicationEventPublisherAware 来手动设置 publisher
package com.demo.controller;

import com.demo.event.ModelExchangeEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/model")
@Slf4j
public class ModelController implements ApplicationEventPublisherAware{

    /**
     * 使用 ApplicationEventPublisher 发布事件
     * */
    private ApplicationEventPublisher ap;

    @RequestMapping("/exchange")
    public void exchange(){
        log.info("伪代码:模型变更的逻辑");
        this.ap.publishEvent(new ModelExchangeEvent(ap, 9001, 2));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.ap = applicationEventPublisher;
    }
}

跟上面差距不大

3. 重要组件说明

1. ApplicationEvent

Spring中所有事件的基类,继承自java.util.EventObject。开发人员可以通过继承ApplicationEvent类来创建自定义事件,在事件对象中封装相关信息。事件可以同步或异步触发,并支持条件事件和层次事件等特性。

2. ApplicationListener

是一个监听器接口,用于处理特定类型的事件。当被感兴趣的事件发生时,容器会调用相应的监听器方法,传入该事件作为参数。开发人员可以实现ApplicationListener接口来创建自己的监听器,然后使用@EventListener注解或者配置文件的方式进行注册。

3. ApplicationEventMulticaster

是一个事件广播器,将事件发送给所有已注册的ApplicationListener。默认情况下,Spring使用SimpleApplicationEventMulticaster实现事件广播,但也可以通过配置使用其他实现方式,例如AsyncApplicationEventMulticaster和SimpleThreadScopeEventMulticaster。
就是通过这玩意来调用每个 listener 的处理逻辑。

4. ApplicationEventPublisher

发布者,通过这个组件来发布事件。

4. 多个监听者执行顺序

默认情况下多个监听者同步执行,使用 @Order 注解标注执行顺序。

5. 异步调用

也有两种方式。

1. ApplicationEventMulticaster

package com.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;

/**
 * 配置监听器异步执行
 * */
@Configuration
public class ListenerConfig {

    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() { //@1
        //创建一个事件广播器
        SimpleApplicationEventMulticaster result = new SimpleApplicationEventMulticaster();
        //给广播器提供一个线程池,通过这个线程池来调用事件监听器
        Executor executor = this.applicationEventMulticasterThreadPool().getObject();
        //设置异步执行器
        result.setTaskExecutor(executor);//@1
        return result;
    }

    //提供一个线程池
    @Bean
    public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() {
        ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();
        result.setThreadNamePrefix("modelThread-");
        result.setCorePoolSize(2);
        return result;
    }
}

  • 原理

  自定义 ApplicationEventMulticaster,并设置其线程池,监听器最终是通过ApplicationEventMulticaster内部的实现来调用的,所以我们关注的重点就是这个类,这个类默认有个实现类 SimpleApplicationEventMulticaster,这个类是支持监听器异步调用的,内部有个线程池字段:

private Executor taskExecutor;

SimpleApplicationEventMulticaster 执行逻辑:

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) { 
            //线程池对象不为 null,就提交自定义监听事件到线程池中
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            //线程池对象为 null,就顺序执行每个 listener 自定义监听事件
            invokeListener(listener, event);
        }
    }
}

所以我们需要定义一个 SimpleApplicationEventMulticaster 的 bean 并设置其线程池.

  • 弊端:这样会使所有的监听器都异步执行

2. @Async

@EnableAsync 标注在启动类上,来标识支持异步。@Async 标注在指定 listener 上。这种应该就是传说中 bean 的异步执行。

package com.demo.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 伪代码:着色继承
 * 实现 ApplicationListener 方式
 * */
@Component
@Slf4j
@Async
public class ModelExchangeColorListener implements ApplicationListener<ModelExchangeEvent> {
    @Override
    public void onApplicationEvent(ModelExchangeEvent e) {
        log.info("收到模型{}版本{}的变更通知!着色继承开始!", e.getModelId(), e.getModelVersion());
        //模拟逻辑运行
        try {
            Thread.sleep(2_000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        log.info("着色继承结束!");
    }
}
package com.demo.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 伪代码:继承
 * 使用 @EventListener 方式
 * */
@Component
@Slf4j
public class ModelExchangePropertiesListener {

    /**
     * 属性继承
     * */
    @EventListener(ModelExchangeEvent.class)
    @Async
    public void onEvent(ModelExchangeEvent e){
        log.info("收到模型{}版本{}的变更通知!自定义属性继承开始!", e.getModelId(), e.getModelVersion());
        //模拟逻辑运行
        try {
            Thread.sleep(2_000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        log.info("自定义属性继承结束!");
    }

    /**
     * 分享继承
     * */
    @EventListener(ModelExchangeEvent.class)
    @Async
    public void sss(ModelExchangeEvent e){
        log.info("收到模型{}版本{}的变更通知!分享继承开始!", e.getModelId(), e.getModelVersion());
        //模拟逻辑运行
        try {
            Thread.sleep(2_000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        log.info("分享继承结束!");
    }
}

package com.demo;

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

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

  • 默认的线程池 SimpleAsyncTaskExecutor 不太行,可以通过 @Async("applicationEventMulticasterThreadPool") 自己换一个。
package com.demo.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 伪代码:着色继承
 * 实现 ApplicationListener 方式
 * */
@Component
@Slf4j
@Async("applicationEventMulticasterThreadPool")
public class ModelExchangeColorListener implements ApplicationListener<ModelExchangeEvent> {
    @Override
    public void onApplicationEvent(ModelExchangeEvent e) {
        log.info("收到模型{}版本{}的变更通知!着色继承开始!", e.getModelId(), e.getModelVersion());
        //模拟逻辑运行
        try {
            Thread.sleep(2_000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        log.info("着色继承结束!");
    }
}

自定义的线程池

package com.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;

import java.util.concurrent.Executor;

/**
 * 配置监听器异步执行
 * */
@Configuration
public class ListenerConfig {
    //提供一个线程池
    @Bean
    public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() {
        ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();
        result.setThreadNamePrefix("modelThread-");
        result.setCorePoolSize(2);
        return result;
    }
}

参考文献

https://blog.csdn.net/lin1214000999/article/details/124707979
原理参考:https://cloud.tencent.com/developer/article/2364790

posted @ 2024-03-08 17:47  primaryC  阅读(95)  评论(0编辑  收藏  举报