聊一聊Spring中的自定义监听器

前言

通过一个简单的自定义的监听器,从源码的角度分一下Spring中监听的整个过程,分析监听的作用。

一、自定义监听案例

1.1定义事件

java 复制代码
package com.lazy.snail;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

/**
 * @ClassName UserRegisteredEvent
 * @Description TODO
 * @Author lazysnail
 * @Date 2024/11/8 10:37
 * @Version 1.0
 */
@Getter
public class UserRegisteredEvent extends ApplicationEvent {
    private final String username;

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

1.2定义监听

java 复制代码
package com.lazy.snail;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * @ClassName UserRegisteredListener
 * @Description TODO
 * @Author lazysnail
 * @Date 2024/11/8 10:36
 * @Version 1.0
 */
@Component
public class UserRegisteredListener {
    @EventListener
    public void handleUserRegisterEvent(UserRegisteredEvent event) {
        System.out.println("用户注册成功,发送邮件通知");
    }
}

1.3定义用户服务(发布事件)

java 复制代码
package com.lazy.snail;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

/**
 * @ClassName UserService
 * @Description TODO
 * @Author lazysnail
 * @Date 2024/11/8 10:37
 * @Version 1.0
 */
@Service
public class UserService {
    private final ApplicationEventPublisher eventPublisher;

    public UserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void registerUser(String username) {
        // 用户注册逻辑
        System.out.println("Registering user: " + username);

        // 发布用户注册事件
        eventPublisher.publishEvent(new UserRegisteredEvent(this, username));
    }
}

1.4测试类

java 复制代码
package com.lazy.snail;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

@Slf4j
public class SpringTest {

    @Test
    void test() {
        ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        context.getBean(UserService.class).registerUser("lazysnail");
    }
}

1.5测试结果

二、事件监听流程

2.1容器启动阶段

2.1.1事件监听方法处理器及默认事件监听工厂

  • 事件监听方法处理器及默认事件监听工厂的bean定义信息注册
    1. 事件监听方法处理器会在后续用于处理自定义监听中的@EventListener注解
    2. 默认事件监听工厂会用于将自定义监听封装为ApplicationListenerMethodAdapter
java 复制代码
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
    // 省略部分代码...
    
    // 事件监听方法处理器
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }
	
    // 默认事件监听工厂
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}
  • 事件监听方法处理器及默认事件监听工厂的实例化
    1. refresh方法中,invokeBeanFactoryPostProcessors处理BeanFactoryPostProcessor(EventListenerMethodProcessor实现了BeanFactoryPostProcessor)
    2. 实例化EventListenerMethodProcessor
    3. 调用EventListenerMethodProcessor的postProcessBeanFactory实例化DefaultEventListenerFactory
java 复制代码
// EventListenerMethodProcessor
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    this.beanFactory = beanFactory;

    Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
    List<EventListenerFactory> factories = new ArrayList<>(beans.values());
    AnnotationAwareOrderComparator.sort(factories);
    this.eventListenerFactories = factories;
}

2.1.3应用事件广播器创建

  • 容器刷新时,initApplicationEventMulticaster创建SimpleApplicationEventMulticaster
  • 注册单例到容器
java 复制代码
// AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {
    // 为容器初始化事件广播器
	initApplicationEventMulticaster();
}
java 复制代码
// AbstractApplicationContext
protected void initApplicationEventMulticaster() {
    this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
    beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    if (logger.isTraceEnabled()) {
        logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
    }
}
  • SimpleApplicationEventMulticaster从AbstractApplicationEventMulticaster继承过来一个defaultRetriever对象
  • defaultRetriever中封装了监听器集合
java 复制代码
private class DefaultListenerRetriever {

    public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

    public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
}
  • 监听集合中的监听是何时添加的

提前实例化单例后EventListenerMethodProcessor对容器中所有监听处理时添加

java 复制代码
// DefaultListableBeanFactory
public void preInstantiateSingletons() throws BeansException {
    // 省略部分代码...
	
    // EventListenerMethodProcessor
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
                    .tag("beanName", beanName);
            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    smartSingleton.afterSingletonsInstantiated();
                    return null;
                }, getAccessControlContext());
            } else {
                // 单例实例化后处理
                smartSingleton.afterSingletonsInstantiated();
            }
            smartInitialize.end();
        }
    }
}
  • 监听器的创建
java 复制代码
// EventListenerMethodProcessor
public void afterSingletonsInstantiated() {
    ConfigurableListableBeanFactory beanFactory = this.beanFactory;
    Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
    String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
    // 处理UserRegisteredListener
    for (String beanName : beanNames) {
        // 省略部分代码...
        processBean(beanName, type);
    }
}

private void processBean(final String beanName, final Class<?> targetType) {
    if (!this.nonAnnotatedClasses.contains(targetType) &&
            AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
            !isSpringContainerClass(targetType)) {

        Map<Method, EventListener> annotatedMethods = null;
        // 省略部分代码...
        
        // @EventListener注解的方法(注解上的属性)
        annotatedMethods = MethodIntrospector.selectMethods(targetType,
                    (MethodIntrospector.MetadataLookup<EventListener>) method ->
                            AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));

        if (CollectionUtils.isEmpty(annotatedMethods)) {
            this.nonAnnotatedClasses.add(targetType);
            if (logger.isTraceEnabled()) {
                logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
            }
        } else {
            // Non-empty set of methods
            ConfigurableApplicationContext context = this.applicationContext;
            Assert.state(context != null, "No ApplicationContext set");
            List<EventListenerFactory> factories = this.eventListenerFactories;
            Assert.state(factories != null, "EventListenerFactory List not initialized");
            for (Method method : annotatedMethods.keySet()) {
                for (EventListenerFactory factory : factories) {
                    if (factory.supportsMethod(method)) {
                        Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
                        // 事件监听工厂创建应用监听器 ApplicationListenerMethodAdapter
                        ApplicationListener<?> applicationListener =
                                factory.createApplicationListener(beanName, targetType, methodToUse);
                        if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                            ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                        }
                        // 添加到应用上下文
                        context.addApplicationListener(applicationListener);
                        break;
                    }
                }
            }
        }
    }
}

2.2客户端调用阶段

  • 发布事件
java 复制代码
// AbstractApplicationContext
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
  • 拿到内部应用事件广播器(SimpleApplicationEventMulticaster)

  • 广播器广播事件

java 复制代码
// SimpleApplicationEventMulticaster
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);
        }
    }
}
  • 获取监听
    1. 检索应用监听器
    2. 直接从检索器(defaultRetriever)中取出监听
java 复制代码
/**
 * 根据给定的事件、源(我理解是容器)检索监听器
 * 
 */
// AbstractApplicationEventMulticaster
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

    List<ApplicationListener<?>> allListeners = new ArrayList<>();
    Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
    Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);

    Set<ApplicationListener<?>> listeners;
    Set<String> listenerBeans;
    synchronized (this.defaultRetriever) {
        // 默认检索器中获取应用监听,监听已经在Spring启动阶段注册完成
        listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
    }

    // 省略部分代码...

    AnnotationAwareOrderComparator.sort(allListeners);
    if (retriever != null) {
        if (filteredListenerBeans.isEmpty()) {
            retriever.applicationListeners = new LinkedHashSet<>(allListeners);
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
        else {
            retriever.applicationListeners = filteredListeners;
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
    }
    return allListeners;
}		
  • 调用监听

invokeListener

三、总结

  • 个人理解:

事件发布是一个抽象的概念,真正将事件发布出去的是SimpleApplicationEventMulticaster,发布事件实际做的事情,找到监听器,过滤出能够处理这个事件的监听器,然后执行监听器中针对这个事件的业务逻辑。

3.1监听流程总结

3.1.1. Spring 容器启动

  • 在 Spring 启动过程中,ApplicationContext 被初始化,它作为核心容器,提供了事件发布和监听的机制。
  • Spring 使用 ApplicationEventPublisher 作为事件发布的核心接口,事件的发布与处理都在 ApplicationContext 内部实现。

3.1.2. 监听器注册

  • 在 Spring 中,可以通过以下几种方式注册监听器:
    • 实现 ApplicationListener 接口:将实现类作为 Spring Bean 注册,Spring 会自动将它识别为事件监听器。
    • 通过 XML 配置 :在 XML 文件中配置 <bean class="com.example.MyEventListener"/>,将监听器注册到 ApplicationContext
    • 注解方式:使用@EventListener注解
  • 监听器的作用 :每当发布的事件类型与监听器泛型参数中的事件类型匹配时,监听器的 onApplicationEvent 方法就会被调用。

3.1.3. 事件发布

  • 发布者 :Spring 中的任何组件都可以通过 ApplicationEventPublisher 发布事件。通常,ApplicationContext 本身实现了 ApplicationEventPublisher,可以直接调用 publishEvent() 发布事件。
  • 事件传播器 :默认情况下,Spring 使用 SimpleApplicationEventMulticaster 作为事件传播器,它负责查找符合条件的监听器并将事件分发给它们。
  • 发布事件的方法 :通过 applicationContext.publishEvent(new CustomEvent(this)) 来发布事件。

3.1.4. 事件广播给监听器

  • 筛选监听器SimpleApplicationEventMulticaster 会检查所有注册的监听器,筛选出对当前事件感兴趣的监听器(基于事件类型的匹配)。
  • 同步与异步 :在 Spring 环境中,默认情况下事件是同步传递的,所有监听器在主线程中执行。如果需要异步,可以通过自定义 SimpleApplicationEventMulticaster 并配置线程池。

3.1.5. 监听器处理事件

  • 监听逻辑执行 :每个匹配的监听器会调用 onApplicationEvent() 方法,执行相应的业务逻辑。
  • 异常处理 :如果监听器抛出异常,SimpleApplicationEventMulticaster 会捕获并记录日志,但不会影响其他监听器的执行。

3.1.6. 事件传播的扩展

  • 在某些场景中,一个事件的监听器可能会发布新的事件,这会形成事件链。Spring 容器会递归地将这些新事件广播给感兴趣的监听器。

3.2应用场景

3.2.1. 解耦业务逻辑

  • 场景描述:在业务流程中,常常需要在某个操作完成后执行附加逻辑,比如用户注册后发送欢迎邮件、推送通知、或更新统计数据。
  • 实现方式:通过监听器监听用户注册事件,执行后续的附加操作。这样,核心业务逻辑与附加逻辑可以解耦,各自独立管理。
  • 示例 :用户注册成功后触发 UserRegistrationEvent,监听器接收事件后完成发送邮件或通知的任务。

3.2.2. 事务性事件

  • 场景描述:在某些情况下,需要确保只有当事务成功提交后,才会发布事件。比如在订单创建后,确保库存减少或通知支付系统。
  • 实现方式 :通过 @TransactionalEventListener 监听事务性事件,确保事件只有在事务提交成功时才会触发。
  • 示例 :订单创建完成并且数据库事务成功提交后,触发 OrderCreatedEvent,通知库存系统减少库存。

3.2.3. 异步处理任务

  • 场景描述:对于不需要实时完成的任务,可以通过异步监听器来解放主线程,避免阻塞。
  • 实现方式 :在事件监听器方法上使用 @Async,使其在独立线程中执行异步任务。
  • 示例:用户在系统中上传文件,文件处理逻辑通过事件异步执行,以保证上传接口的快速响应。

3.2.4. 应用启动或关闭事件

  • 场景描述:在应用启动或关闭时,通常需要执行一些初始化或清理操作,比如加载配置、检查依赖服务、关闭资源等。
  • 实现方式 :通过监听 ApplicationReadyEventContextClosedEvent 等应用上下文事件,实现启动和关闭时的操作。
  • 示例:在应用启动完成后加载配置文件,或在应用关闭时清理缓存或关闭数据库连接。

3.2.5. 状态变化或监控

  • 场景描述:在系统中监控某些状态的变化,比如监控服务状态、资源使用情况、流量变化等。
  • 实现方式:使用自定义事件来捕获和广播状态变化,监听器实时响应状态变化,执行对应操作。
  • 示例 :当服务发现高负载时,发布 HighLoadEvent,监听器响应并调整系统参数或生成告警。

3.2.6. 领域驱动设计(DDD)中的事件处理

  • 场景描述:在领域驱动设计中,事件驱动架构常用于处理不同领域的事件交互,比如订单模块的事件会影响到支付、物流等模块。
  • 实现方式:通过领域事件(如订单支付事件、库存更新事件)来实现模块间的松耦合通信,避免模块之间的直接依赖。
  • 示例 :在电商系统中,用户下单后触发 OrderPlacedEvent,物流模块监听该事件并安排发货。

3.2.7. 跨服务通信

  • 场景描述:在微服务架构中,服务之间往往需要基于事件进行异步通信,降低耦合度。
  • 实现方式:通过发布事件到消息中间件(如 Kafka、RabbitMQ),各服务监听感兴趣的事件。
  • 示例 :支付服务完成支付后触发 PaymentCompletedEvent,订单服务监听该事件并更新订单状态。

3.2.8. 监听应用配置变化

  • 场景描述:在应用运行期间,可能需要动态刷新配置,比如数据库连接、缓存配置等。
  • 实现方式:通过监听配置中心的配置更新事件,触发配置的刷新。
  • 示例 :当配置中心检测到 Redis 缓存配置更新后触发 CacheConfigUpdateEvent,应用的缓存配置自动刷新。

3.2.9. 处理安全或认证事件

  • 场景描述:在用户认证、权限验证等过程中,可以发布事件来处理安全相关操作。
  • 实现方式:监听认证成功、认证失败等事件,执行相应的业务逻辑,比如记录日志、锁定账户。
  • 示例 :用户多次登录失败后触发 AuthenticationFailureEvent,监听器响应后锁定用户账户并生成告警。
相关推荐
xybm16052 分钟前
MTSET可溶于DMSO、DMF、THF等有机溶剂,并在水中有轻微的溶解性,91774-25-3
java·spring·eclipse·tomcat·1024程序员节
究极无敌暴龙战神6 分钟前
java 面向对象高级
java·开发语言
是老余8 分钟前
Logback 日志介绍及与Spring Boot 的整合 【保姆级教程】
spring boot·后端·logback
dream_ready10 分钟前
四万字长文SpringBoot、Spring、SpringMVC等相关面试题(注:该篇博客将会持续维护 最新维护时间:2024年11月12日)
java·spring boot·后端·spring·面试·1024程序员节
customer0818 分钟前
【开源免费】基于SpringBoot+Vue.JS医疗病历交互系统(JAVA毕业设计)
java·jvm·vue.js·spring boot·后端·spring cloud·kafka
Enoch88832 分钟前
Day09 C++ 存储类
java·jvm·c++
张铁铁是个小胖子1 小时前
显示微服务间feign调用的日志
java·spring·微服务
Aniay_ivy1 小时前
Java中的不可变集合:性能与安全并重的最佳实践
java·windows·安全
Beekeeper&&P...1 小时前
spring中r类是什么
spring·r语言
TomSmile_WorkSpace1 小时前
RabbitMq项目实战--延迟队列实现超时订单处理
开发语言·后端·ruby