自学内容网 自学内容网

Spring事件监听机制

前言

Spring 的事件监听机制,采用了观察者的设计模式。一套完整的事件监听流程是这样的,首先定义事件类,即ApplicationEvent的子类,它包含事件发生的时间戳timestamp和产生事件的来源source,以及自定义的其它事件属性;然后实现事件监听器 ApplicationListener 并注册到容器,订阅感兴趣的事件,Spring 会在事件发生时触发监听器;最后通过事件发布器 ApplicationEventPublisher 发布自定义事件。
在这里插入图片描述

应用场景

事件监听机制的好处主要有两点:

  • 解耦:解耦了事件的发布方和消费方,利于构建高内聚低耦合的模块,程序的扩展性也会更好
  • 异步:监听器可以同步触发,也可以异步触发,对于非主流程的业务可以选择异步触发以获得更快的响应

任何可以晚点做的事,都应该晚点再做。

举个例子,电商系统中,当有新用户注册时,除了要保存用户信息外,还需要额外发送一封邮件通知用户,以及给新用户送一些专属优惠券。
在这个场景下,除了保存用户信息外的其它流程都不是主流程,接口在保存完用户信息后就可以返回结果了,后续的其它业务就可以通过发布事件来处理。

第一步,定义新用户注册的事件类 UserRegisterEvent

public class UserRegisterEvent extends ApplicationEvent {

    private final String username;
    private final String email;

    public UserRegisterEvent(Object source, String username, String email) {
        super(source);
        this.username = username;
        this.email = email;
    }
}

第二步,实现 ApplicationListener,分别处理的是发送邮件和发放优惠券,因为不需要同步执行,我们给方法加上@Async 注解

@Component
public class UserRegisterEmailListener implements ApplicationListener<UserRegisterEvent> {

    @Override
    @Async
    public void onApplicationEvent(UserRegisterEvent event) {
        String content = String.format("欢迎 %s 成为我们的用户", event.getUsername());
        System.err.println("@" + event.getEmail() + ":" + content);
    }
}
@Component
public class NewUserCouponListener implements ApplicationListener<UserRegisterEvent> {

    @Override
    @Async
    public void onApplicationEvent(UserRegisterEvent event) {
        System.err.println("发放新用户优惠券:" + event.getUsername());
    }
}

第三步,用户注册成功后,发布事件

@Service
public class UserService {

    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

    public void register(String username, String password, String email) {
        // save db
        UserRegisterEvent event = new UserRegisterEvent(this, username, email);
        applicationEventPublisher.publishEvent(event);
    }
}

设计实现

事件监听机制由四个组件构成:

  • 事件类 ApplicationEvent 负责定义事件本身
  • 事件发布器 ApplicationEventPublisher 负责发布事件
  • 事件多播器 ApplicationEventMulticaster 根据订阅规则,触发事件对应的监听器
  • 事件监听器 ApplicationListener 负责处理事件

Spring 的事件监听机制,遵循了JDK的开发规范,所有的事件类都派生自java.util.EventObject ,所有的监听器都派生自java.util.EventListener

ApplicationEventPublisher 只是套了一层壳,发布事件会委托给 AbstractApplicationContext,主要做了两件事:

  • 委托给 ApplicationEventMulticaster 派发事件
  • 如果存在父容器,在父容器里发布事件
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
} else {
// 非ApplicationEvent子类 封装成PayloadApplicationEvent
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
// 多播事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 如果存在父容器,也发布事件
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}

ApplicationEventMulticaster 的默认实现是 SimpleApplicationEventMulticaster,它首先会解析 event 对象的类型,然后查找该事件类型对应的监听器,最后触发监听器事件。

@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) {
executor.execute(() -> invokeListener(listener, event));
} else {
// 触发监听器
invokeListener(listener, event);
}
}
}

判断ApplicationListener是否能处理某个类型的event开销较大,Spring 解析一次后会把他们的缓存起来,容器是ConcurrentHashMap,其中 ListenerCacheKey 封装的是 eventType 和 sourceType,CachedListenerRetriever 封装的是一组 applicationListeners。

final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

最后还剩一个问题,ApplicationEventMulticaster 维护了 EventType 和 ApplicationListener 的关系,ApplicationListener 是什么时候被注册到 ApplicationEventMulticaster 的?
通过源码发现,Spring 内置了一个 ApplicationListenerDetector 类,它是 BeanPostProcessor 的子类,重写了 postProcessAfterInitialization() 方法,Spring 容器内所有的 Bean 初始化完成后,都会判断是不是 ApplicationListener 的子类,如果是就会把 Bean 注册到 ApplicationEventMulticaster。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
Boolean flag = this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
} else if (Boolean.FALSE.equals(flag)) {
this.singletonNames.remove(beanName);
}
}
return bean;
}

尾巴

Spring框架提供了一个强大的事件监听机制,允许应用程序的不同组件之间进行松耦合的通信。事件的发布方和消费方都无需知道对方的存在,双方利用事件来通信,利于应用程序中实现模块化、可扩展和可重用的组件。另外,对于非主流程的业务逻辑,事件消费方可以选择异步处理,以获得更好的响应速度。


原文地址:https://blog.csdn.net/qq_32099833/article/details/136460616

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!