spring event事件通知机制

spring容器提供一种事件发布通知机制用来实现简单的消息通信,达到一定的业务解耦,开发人员可以使用Spring事件框架来定义和处理各种事件,是一种观察者模式的实现。

事件定义

要发布事件首先要定义事件,我们可以通过集成ApplicationEvent类来定义事件

java 复制代码
public class MyEvent extends ApplicationEvent {
    public MyEvent(Object source) {
        super(source);
    }
}

需要调用ApplicationEvent类构造函数来设置消息内容。

事件发布

事件的发布我们可以通过实现ApplicationEventPublisherAware接口,重写其setApplicationEventPublisher方法获取事件发布器。然后调用发布器的publishEvent方法发布事件

如下发布上面自定义的MyEvent事件

java 复制代码
@Service
@Slf4j
public class EventService implements ApplicationEventPublisherAware {

    //@Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    //从写setApplicationEventPublisher 获取发布器
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void publish(String msg){
        log.info("开始发送事件");
        MyEvent event = new MyEvent(msg);
        /**
         * 默认事件的发送是同步处理
         * 所以要在事件处理里控制好异常,否则会影响当前发布方法
         */
        applicationEventPublisher.publishEvent(event);
        log.info("结束事件处理");
    }
}

这里setApplicationEventPublisher方法传入的publisher实际上就是context上下文,因为context实现了ApplicationEventPublisher接口。实现了ApplicationEventPublisherAware接口的bean会在ApplicationContextAwareProcessor后置处理器的before方法处调用其接口方法,完成回调赋值。

除了通过实现接口的方式,也可以直接使用@Autowired注解来注入事件发布器。

发布器的publishEvent(event)方法默认是同步方式进行调用的,所以在事件处理方法里一定要处理好异常,以免影响我们主业务逻辑。

除了同步方式调用,还可以配置为异步方式

如下自定义@Bean(name = "applicationEventMulticaster"),设置其taskExecutor即可。

java 复制代码
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
    SimpleApplicationEventMulticaster eventMulticaster =
            new SimpleApplicationEventMulticaster();

    eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
    return eventMulticaster;
}

具体原理在后面源码分析解读。

事件处理

定义消息处理器可以通过实现ApplicationListener接口来完成,该接口支持泛型指定一个具体类型消息E extends ApplicationEvent。当有匹配消息是,其onApplicationEvent方法会被调用。

如下定义处理MyEvent类型消息:

java 复制代码
@Component
@Slf4j
@Order(1)
public class MyEventListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        log.info("receive event:"+event);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用@EventListener注解也可以定义事件监听器
     * @param event
     */
    @EventListener
    @Order(2)
    public void annotationListener(MyEvent event){
        log.info("注解接收事件:"+event);
    }
}

这里看到除了实现ApplicationListener接口外,还可以使用@EventListener注解的方式指定处理其。

如果定义了多个消息处理器,还可以使用 @Order注解来指定调用顺序,小的先执行。

源码分析

spring容器在初始化的过程中,有几步是初始化消息相关操作。来看容器重要初始化方法refresh里几个相关的操作

java 复制代码
//1、初始化事件处理器
initApplicationEventMulticaster();
//2、初始化事件监听器
registerListeners();
//3、发布相关事件
finishRefresh();

1、初始化事件处理器会往beanFacotry添加一个SimpleApplicationEventMulticaster

java 复制代码
protected void initApplicationEventMulticaster() {
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   //这里常量APPLICATION_EVENT_MULTICASTER_BEAN_NAME的值是applicationEventMulticaster
   if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
   }
   else {
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
   }
}

事件发布时候会用到该类进行发布事件。看到这里很容易理解上面自定义applicationEventMulticaster使用异步方式处理消息的配置为什么可行。

2、初始化listener会获取配置的listener

java 复制代码
protected void registerListeners() {
   // Register statically specified listeners first.
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
   }

   //获取定义的ApplicationListener类型的bean
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   for (String listenerBeanName : listenerBeanNames) {
     //将listner存到EventMulticaster里
      getApplicationEventMulticaster里().addApplicationListenerBean(listenerBeanName);
   }
  //...
}

所以我们只要实现ApplicationListener接口,实现其onApplicationEvent方法,当一个事件发布时候,就可以接收到该事件。也可以通过@EventListener注解定义。这里会将所有的消息listeners存放到EventMulticaster里。

java 复制代码
AbstractApplicationEventMulticaster{
  DefaultListenerRetriever {
     Set<ApplicationListener<?>> applicationListeners;
  }
}

addListener最后会存放到applicationListeners变量里。

3、在第三步内部发布相关refresh完成的事件里会调到publishEvent(new ContextRefreshedEvent(this))方法发布一个事件。最后会调到getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);进行事件广播。这里getApplicationEventMulticaster就是上面第一步初始化的SimpleApplicationEventMulticaster。

multicastEvent广播事件方法

java 复制代码
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();//获取执行器
   //获取合适的listeners 广播事件
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {//有执行器就异步执行
         executor.execute(() -> invokeListener(listener, event));
      }
      else {//同步执行
         invokeListener(listener, event);
      }
   }
}

这里的Executor属性可以通过BeanPostProcessor后置拦截器进行赋值。或者根据第1步multicaster的初始化,会先从factory里查找有没有APPLICATION_EVENT_MULTICASTER_BEAN_NAME类型的bean,没有才new一个。所以我们可以显示的声明一个ApplicationEventMulticaster然后设置exexutor属性。

getApplicationListeners会根据消息类型进行listener的过滤

invokeListener就是调用listener.onApplicationEvent(event)方法。

相关推荐
李慕婉学姐1 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
奋进的芋圆3 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin3 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20053 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉3 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国3 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882484 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈4 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_994 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹4 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理