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)方法。

相关推荐
TDengine (老段)8 小时前
TDengine 数学函数 CRC32 用户手册
java·大数据·数据库·sql·时序数据库·tdengine·1024程序员节
心随雨下8 小时前
Tomcat日志配置与优化指南
java·服务器·tomcat
Kapaseker8 小时前
Java 25 中值得关注的新特性
java
wljt8 小时前
Linux 常用命令速查手册(Java开发版)
java·linux·python
撩得Android一次心动8 小时前
Android 四大组件——BroadcastReceiver(广播)
android·java·android 四大组件
canonical_entropy9 小时前
Nop平台到底有什么独特之处,它能用在什么场景?
java·后端·领域驱动设计
chilavert3189 小时前
技术演进中的开发沉思-174 java-EJB:分布式通信
java·分布式
不是株9 小时前
JavaWeb(后端进阶)
java·开发语言·后端
编程火箭车9 小时前
【Java SE 基础学习打卡】02 计算机硬件与软件
java·电脑选购·计算机基础·编程入门·计算机硬件·软件系统·编程学习路线