前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring源码浅析——事件和异步事件

Spring源码浅析——事件和异步事件

作者头像
用户1413827
发布2023-11-28 15:37:51
3050
发布2023-11-28 15:37:51
举报
文章被收录于专栏:站长运维

Spring源码浅析——事件和异步事件

一、背景知识
观察者模式

观察者模式(Observer Pattern)是一种设计模式,用于在对象之间定义一种一对多的依赖关系,以便当一个对象的状态发生变化时,所有依赖于它的其他对象都能够自动接收通知并做出相应的处理。

在观察者模式中,有两种核心角色:

  1. 主题(Subject):表示被观察的对象,它维护了一组观察者对象,并提供添加、删除和通知观察者的方法。
  2. 观察者(Observer):表示接收主题通知的对象,它包含了需要在主题状态发生变化时执行的业务逻辑。

观察者模式的优点包括:

  1. 松耦合:主题和观察者之间的依赖关系是松散的,使得它们可以独立地进行修改和扩展。
  2. 可重用性:主题和观察者可以在多个对象之间共享,从而提高代码的重用性。
  3. 易于扩展:可以随时添加或删除观察者对象,而不会影响到主题和其他观察者对象。

观察者模式在实际应用中非常常见,例如GUI界面中的事件监听器、消息队列中的消费者等。同时,观察者模式也是许多其他设计模式的基础,例如MVC(Model-View-Controller)模式、发布-订阅模式等。

Spring事件体系

Spring事件体系包括以下三个主要组件:

  1. ApplicationEvent:表示应用程序中发生的事件,是一个抽象基类。
  2. ApplicationListener:监听器接口,定义了在收到特定类型的事件时需要执行的操作。
  3. ApplicationEventMulticaster:事件广播器,将事件发送给所有已注册的ApplicationListener。

下面分别对它们进行简要介绍:

  1. ApplicationEvent:Spring中所有事件的基类,继承自java.util.EventObject。开发人员可以通过继承ApplicationEvent类来创建自定义事件,在事件对象中封装相关信息。事件可以同步或异步触发,并支持条件事件和层次事件等特性。
  2. ApplicationListener:是一个监听器接口,用于处理特定类型的事件。当被感兴趣的事件发生时,容器会调用相应的监听器方法,传入该事件作为参数。开发人员可以实现ApplicationListener接口来创建自己的监听器,然后使用@EventListener注解或者配置文件的方式进行注册。
  3. ApplicationEventMulticaster:是一个事件广播器,将事件发送给所有已注册的ApplicationListener。默认情况下,Spring使用SimpleApplicationEventMulticaster实现事件广播,但也可以通过配置使用其他实现方式,例如AsyncApplicationEventMulticaster和SimpleThreadScopeEventMulticaster。

总之,Spring事件体系提供了一种可扩展、易于使用的机制来实现内部应用程序事件处理,它可以帮助开发人员实现松耦合、可重用和高效的应用程序设计。

Spring事件实例

以下是一个简单的Spring事件实例,其中定义了一个自定义事件MyEvent和相应的监听器MyEventListener:

首先,定义一个MyEvent类作为自定义事件:

代码语言:javascript
复制
package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    private String message;

    public MyEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

接下来,定义一个MyEventListener类作为事件监听器:

代码语言:javascript
复制
package com.example.demo.event;

import org.springframework.context.ApplicationListener;

public class MyEventListener implements ApplicationListener<MyEvent> {

    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("Received MyEvent - " + event.getMessage());
    }
}

最后,在Spring配置文件中注册事件监听器并触发自定义事件:

代码语言:javascript
复制
<bean id="myEventListener" class="com.example.demo.event.MyEventListener"/>

<bean id="eventPublisher" class="org.springframework.context.support.ApplicationObjectSupport">
    <property name="applicationEventPublisher" ref="applicationContext"/>
</bean>

<bean id="myBean" class="com.example.demo.MyBean">
    <property name="eventPublisher" ref="eventPublisher"/>
</bean>

在MyBean类中,定义了一个触发自定义事件的方法:

代码语言:javascript
复制
package com.example.demo;

import com.example.demo.event.MyEvent;
import org.springframework.context.ApplicationEventPublisher;

public class MyBean {

    private ApplicationEventPublisher eventPublisher;

    public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void doSomething(String message) {
        System.out.println("Doing something with " + message);
        MyEvent event = new MyEvent(this, message);
        eventPublisher.publishEvent(event);
    }
}

在上述示例中,MyBean类通过调用ApplicationEventPublisher的publishEvent()方法触发自定义事件MyEvent,并将事件传递给相应的监听器MyEventListener进行处理。在这个过程中,Spring事件体系为我们提供了一种简单而有效的机制来实现内部应用程序事件的处理。

如果运行上述示例,其输出将如下所示:

代码语言:javascript
复制
Doing something with Hello World
Received MyEvent - Hello World

这表明当MyBean的doSomething()方法被调用时,它会发布一个自定义事件MyEvent,并将事件传递给监听器MyEventListener进行处理。MyEventListener将收到该事件并在控制台上输出相应消息“Received MyEvent - Hello World”。

二、Spring事件原理

下面是refresh()函数的源代码实现,可以看到其中包括了事件监听器的注册和发布:

代码语言:javascript
复制
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context. 主要看这句
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them. 主要看这句,注册事件监听器,监听器需要实现ApplicationListener接口
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

在refresh()方法中,可以看到以下事件相关的代码:

  • prepareRefresh()方法中注册了StandardEnvironment中的事件监听器;
  • registerListeners()方法中注册了所有实现了ApplicationListener接口的bean为事件监听器;
  • finishRefresh()方法中发布了ContextRefreshedEvent事件。

因此,refresh()函数是Spring事件机制的重要组成部分之一。

2.1 初始化事件广播

在Spring中,初始化事件广播是通过ApplicationContext接口的refresh()方法来触发的。在refresh()方法中,会实例化并注册一个ApplicationEventMulticaster实例,该实例用于管理所有事件监听器并负责事件的发布。

下面是AbstractApplicationContext类中refresh()方法的部分源代码:

代码语言:javascript
复制
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        configureBeanFactory(beanFactory);
        // 在这里实例化并注册了一个ApplicationEventMulticaster实例
        addBeanFactoryPostProcessors(beanFactory);
        registerBeanPostProcessors(beanFactory);
        initMessageSource();
        initApplicationEventMulticaster(); // 初始化ApplicationEventMulticaster
        onRefresh();
        registerListeners(); // 注册事件监听器
        finishBeanFactoryInitialization(beanFactory);
        finishRefresh();
    }
    catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
        }
        destroyBeans();
        cancelRefresh(ex);
        throw ex;
    }
}

在上述代码中,可以看到initApplicationEventMulticaster()方法被调用了,它实例化并注册了一个ApplicationEventMulticaster实例。这个实例是SimpleApplicationEventMulticaster类的一个对象,用于多播事件给所有的事件监听器。

同时,在registerListeners()方法中,会将所有实现了ApplicationListener接口的bean注册为事件监听器,以便在事件发生时能够及时处理。

这样,在refresh()方法中,初始化了事件广播机制和注册了所有的事件监听器,当有事件发生时,可以将事件多播给所有的事件监听器进行处理。

2.2 注册事件监听器

在Spring中,注册事件监听器是通过调用AbstractApplicationContext.registerListeners()方法来实现的。该方法会遍历所有的bean,在其中查找并注册实现了ApplicationListener接口的bean。

下面是registerListeners()方法的部分源代码:

代码语言:javascript
复制
protected void registerListeners() {
    // 获取事件广播器
    ApplicationEventMulticaster multicaster = getApplicationEventMulticaster();
    for (String listenerBeanName : getBeanNamesForType(ApplicationListener.class, true, false)) {
        // 将实现了ApplicationListener接口的bean添加到事件广播器中
        multicaster.addApplicationListenerBean(listenerBeanName);
    }

    // 扫描所有的listener bean,并将它们添加到事件广播器中
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        addApplicationListenerBean(listenerBeanName);
    }

    // 扫描所有的listener类,并将它们添加到事件广播器中
    String[] listenerClasses = getApplicationListenerClasses();
    for (String listenerClass : listenerClasses) {
        multicaster.addApplicationListener(new SimpleApplicationListenerAdapter(beanFactory -> {
            try {
                return beanFactory.getBean(listenerClass, ApplicationListener.class);
            }
            catch (BeansException ex) {
                throw new IllegalStateException("Could not load listener class: " + listenerClass, ex);
            }
        }));
    }
}

在上述代码中,可以看到以下关键步骤:

  1. 通过getApplicationEventMulticaster()方法获取事件广播器。
  2. 在遍历bean时,如果发现一个bean实现了ApplicationListener接口,则将其添加到事件广播器中。
  3. 在遍历完所有bean后,再次扫描所有的listener bean,并将它们也添加到事件广播器中。
  4. 最后,扫描所有的listener类,并将它们添加到事件广播器中。

在上述代码的执行过程中,可以看到使用了两个关键组件:

  • ApplicationEventMulticaster:负责管理所有事件监听器并多播事件给它们。
  • ListenerRetriever:用于从bean中检索事件监听器。

其中,ApplicationEventMulticaster是使用SimpleApplicationEventMulticaster实现的。而ListenerRetriever则在DefaultListableBeanFactory中实现,用于从bean中检索事件监听器。

总之,注册事件监听器是Spring中关键的事件管理机制之一。通过自动扫描并识别实现了ApplicationListener接口的bean,并将它们注册到内部的事件广播器中,以便在事件发生时能够及时处理。

2.3 发布事件

在Spring中,发布事件是通过ApplicationContext接口的publishEvent()方法来实现的。当应用程序需要发布一个事件时,只需要调用该方法并传入相应的事件对象即可。

下面是ApplicationContext接口的publishEvent()方法的部分源代码:

代码语言:javascript
复制
void publishEvent(ApplicationEvent event);

default void publishEvent(Object event) {
    publishEvent((ApplicationEvent) event);
}

在上述代码中,可以看到publishEvent()方法会接收一个ApplicationEvent类型的参数,并使用默认方法将其转换为Object类型的参数再次调用publishEvent()方法。

publishEvent()方法内部主要完成以下几个步骤:

  1. 获取当前应用程序上下文对象。
  2. 调用getApplicationEventMulticaster()方法获取事件广播器。
  3. 创建一个GenericApplicationEvent对象,并设置其source和event对象。
  4. 调用事件广播器的multicastEvent()方法多播事件给所有的监听器。

下面是关键步骤的部分源代码:

代码语言:javascript
复制
public void publishEvent(ApplicationEvent event) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }
    getApplicationEventMulticaster().multicastEvent(toApplicationEvent(event));
}

protected ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
    // 获取事件广播器
    ApplicationEventMulticaster multicaster = this.applicationEventMulticaster;
    if (multicaster == null) {
        throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
                                            "call 'refresh' before multicasting events via the context: " + this);
    }
    return multicaster;
}

protected ApplicationEvent toApplicationEvent(Object event) {
    // 如果传入的event已经是一个ApplicationEvent,则直接返回
    if (event instanceof ApplicationEvent) {
        return (ApplicationEvent) event;
    }

    // 否则创建一个GenericApplicationEvent对象,并将event作为payload
    return new PayloadApplicationEvent<>(this, event);
}

在上述代码中,可以看到以下关键步骤:

  • getApplicationEventMulticaster()方法中获取事件广播器。
  • toApplicationEvent()方法中将传递的事件转换为一个ApplicationEvent对象。

最重要的是,在publishEvent()方法中调用了multicastEvent()方法,该方法会将事件多播给所有注册的事件监听器。具体来说,该方法会创建一个SimpleApplicationEventMulticaster实例,并调用其中的invokeListener()方法处理每一个监听器。

总之,Spring的事件机制通过使用publishEvent()方法发布事件,并使用ApplicationEventMulticasterListenerRetriever管理和检索事件监听器,从而实现了事件驱动的应用程序设计。

2.4 异步事件

以下是使用Spring的ApplicationEventMulticaster实现一个异步事件的示例代码:

代码语言:javascript
复制
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class MyEventAsyncPublisher {
    
    private ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
    private Executor executor = Executors.newFixedThreadPool(5); // 可以设置线程池大小

    public void publishEventAsynchronously(ApplicationEvent event) {
        executor.execute(() -> {
            eventMulticaster.multicastEvent(event);
        });
    }

    public void addApplicationListener(ApplicationListener<?> listener) {
        eventMulticaster.addApplicationListener(listener);
    }
}

这个类中,我们创建了一个SimpleApplicationEventMulticaster对象作为事件广播器,并使用了Executors.newFixedThreadPool()方法创建了一个含有5个线程的线程池。在publishEventAsynchronously()方法中,我们通过将事件发布转换为异步执行任务来实现异步事件的处理。最后,我们提供了一个addApplicationListener()方法来添加事件监听器。

三、基于注解的事件与异步事件

Spring从4.2版本开始支持基于注解的事件和异步事件处理。其中,@EventListener注解用于标记一个方法为事件监听器,该方法会在相应的事件发生时被调用。可以使用@Async注解将这个方法转换为异步执行。

以下是一个基于注解的事件监听器的例子:

代码语言:javascript
复制
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventListener {

    @EventListener
    public void handleMyCustomEvent(MyCustomEvent event) {
        // 处理 MyCustomEvent 事件
    }
}

在这个例子中,我们使用了@Component注解将MyEventListener类声明为一个Spring组件,并使用@EventListener注解标记了handleMyCustomEvent()方法作为一个事件监听器来处理MyCustomEvent类型的事件。

如果我们想要让这个事件监听器具有异步执行的能力,只需要在handleMyCustomEvent()方法上添加@Async注解即可:

代码语言:javascript
复制
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class MyAsyncEventListener {
    
    @Async
    @EventListener
    public void handleMyCustomEvent(MyCustomEvent event) {
        // 异步处理 MyCustomEvent 事件
    }
}

在这个例子中,我们在@EventListener注解之前加上了@Async注解,表示希望Spring异步地处理这个事件。注意,这里还需要在配置类中开启异步处理,例如:

代码语言:javascript
复制
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AppConfig {
    // 配置类代码
}

除了使用@EventListener注解外,我们还可以通过实现ApplicationListener接口,并在onApplicationEvent()方法中处理事件来自定义事件监听器。如果需要异步执行,则需要将这个监听器转换为异步执行,例如:

代码语言:javascript
复制
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class MyAsyncEventListener implements ApplicationListener<MyCustomEvent> {

    @Override
    @Async
    public void onApplicationEvent(MyCustomEvent event) {
        // 异步处理 MyCustomEvent 事件
    }
}

在这个例子中,我们实现了ApplicationListener接口,并在onApplicationEvent()方法中处理MyCustomEvent类型的事件。同时,在方法上添加了@Async注解,表示希望Spring异步地处理这个事件。

3.1 @EventListener 原理

在Spring框架的AbstractApplicationContext类中确实没有preInstantiateSingletons()方法。

这个方法实际上是定义在DefaultListableBeanFactory类中的,用于预先实例化所有非懒加载的单例Bean对象。在应用程序上下文初始化时,AbstractApplicationContext会将BeanFactory委托给子类DefaultListableBeanFactory来完成BeanDefinition的加载、解析和注册等工作,因此preInstantiateSingletons()方法也就是在DefaultListableBeanFactory类中被调用的。

以下是DefaultListableBeanFactory类中preInstantiateSingletons()方法的源代码:

代码语言:javascript
复制
@Override
public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }

    // 遍历所有的bean name
    //到这里所有非懒加载的singleton beans已经完成初始化
    //如果我们定义的bean是实现了SmartInitializingSingleton接口的,那么在这里回调它的afterSingletonsInstantiated方法,通过名字可知道它表示单例对象初始化后需要做的操作
    for (String beanName : this.beanDefinitionNames) {
        // 获取bean definition
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        // 判断是否是单例的且非懒加载
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            // 判断是否是factory bean,如果是则不需要实例化
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    final FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                                ((SmartFactoryBean<?>) factory)::isEagerInit,
                                getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit()) ||
                                !requiresEagerInitForType(factory.getObjectType());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
            }
            else {
                // 非factory bean直接实例化
                getBean(beanName);
            }
        }
    }

    // Publish the final list of singleton beans.
    this.frozenBeanDefinitionNames = null;
    this.frozenBeanDefinitionCount = 0;
    if (logger.isTraceEnabled()) {
        logger.trace("Finished pre-instantiating singletons in " + this);
    }
}

需要注意的是,preInstantiateSingletons()方法会在应用程序上下文初始化后被调用,因此它不属于@EventListener注解的实现逻辑。

对于Spring中SmartInitializingSingleton和EventListenerMethodProcessor接口以及事件相关的源代码,我来做一下分析:

首先,SmartInitializingSingleton是一个回调接口,它定义了一个afterSingletonsInstantiated()方法。在Spring容器初始化完成所有Bean对象实例化之后,会回调实现该接口的Bean对象的afterSingletonsInstantiated()方法。

EventListenerMethodProcessor实现了SmartInitializingSingleton接口,并且主要负责处理标记有@EventListener注解的Bean对象。具体来说,它会遍历所有BeanDefinition,检查是否存在带有@EventListener注解的方法,如果存在,则将其封装成ApplicationListenerAdapter对象,并将其注册到事件派发器中。

ApplicationListenerAdapter是一个适配器类,用于将普通的Java方法转换为Spring的监听器。它实现了ApplicationListener接口,并包装了一个目标方法(标记有@EventListener注解的方法),当事件被派发时,它会自动调用目标方法来处理事件。

而事件的派发是由事件派发器(ApplicationEventMulticaster)来实现的。事件派发器维护着一张事件类型与监听器列表的映射表,当事件被发布时,事件派发器会根据事件类型找到相应的监听器列表,并依次调用每个监听器的onApplicationEvent()方法来处理事件。

以下是EventListenerMethodProcessor类中的部分源代码,可以看到它是如何实现将标记有@EventListener注解的Bean对象注册为事件监听器的:

代码语言:javascript
复制
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {

    private final Map<EventListenerFactory, Set<String>> eventListenerFactories = new HashMap<>();

    private final ApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster();

    @Override
    public void afterSingletonsInstantiated() {
        synchronized (this.lifecycleMonitor) {
            for (final Map.Entry<EventListenerFactory, Set<String>> entry : this.eventListenerFactories.entrySet()) {
                EventListenerFactory listenerFactory = entry.getKey();
                Set<String> beanNames = entry.getValue();
                for (String beanName : beanNames) {
                    addApplicationListener(listenerFactory.createApplicationListener(beanName));
                }
            }
            this.eventListenerFactories.clear();
        }
    }

    protected void addApplicationListener(ApplicationListener<?> listener) {
        this.applicationEventMulticaster.addApplicationListener(listener);
    }

    // ...
}

需要注意的是,在Spring中,除了@EventListener注解之外,还有很多其他机制可以处理事件,例如自定义事件、发布/订阅模型等。开发人员可以根据实际需求选择合适的事件处理方式来完成应用程序的编写。

这些代码中包含了以下两个逻辑:

  1. 扫描所有Bean对象,将带有@EventListener注解的方法注册为事件监听器

在Spring框架中,可以通过在Bean对象的方法上标记@EventListener注解来定义事件监听器。当事件被发布时,对应的监听器会自动接收到事件通知,并执行相应的处理逻辑。

在实现上,Spring容器会扫描所有的Bean对象,查找标记有@EventListener注解的方法,并将其封装成ApplicationListenerAdapter对象。然后,将这些ApplicationListenerAdapter对象注册到全局的事件派发器中(ApplicationEventMulticaster),以便在事件被发布时能够正确地调用相应的监听器。

  1. 实现SmartInitializingSingleton接口,在所有Bean对象初始化完成后进行事件监听器的注册

SmartInitializingSingleton是Spring框架提供的一个回调接口,它定义了一个afterSingletonsInstantiated()方法。在所有Bean对象初始化完成之后,Spring容器会回调所有实现了该接口的Bean对象的afterSingletonsInstantiated()方法。

EventListenerMethodProcessor实现了SmartInitializingSingleton接口,并且主要负责将@EventListener注解注册为事件监听器。具体来说,它会在afterSingletonsInstantiated()方法中遍历所有BeanDefinition,检查是否存在带有@EventListener注解的方法,如果存在,则将其封装成ApplicationListenerAdapter对象,并将其注册到全局的事件派发器中。

需要注意的是,由于在afterSingletonsInstantiated()方法中注册事件监听器,因此所有Bean对象的初始化必须在该方法被调用之前完成。否则,在事件发布时可能会出现一些问题,例如无法正确地找到对应的监听器或者无法正确地处理事件。

  1. 查找当前Bean标注了@EventListener的方法

在BeanFactoryPostProcessor接口中实现类EventListenerRegistrySupport中的processEventListeners()方法中,会遍历所有的BeanDefinition,检查是否存在带有@EventListener注解的方法,并将其添加到事件监听器工厂列表中。具体实现代码如下:

代码语言:javascript
复制
@Override
public void processEventListeners(
        final GenericApplicationContext applicationContext, final List<EventListenerFactory> factories) {
    ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
    String[] beanNames = beanFactory.getBeanDefinitionNames();
    for (String beanName : beanNames) {
        BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
        Class<?> beanType = beanFactory.getType(beanName);
        if (beanType != null && !bd.isAbstract() && bd.hasMethodOverrides()) {
            MethodOverrides methodOverrides = bd.getMethodOverrides();
            Set<MethodMetadata> annotatedMethods = methodOverrides.getAnnotatedMethods(EventListener.class.getName());
            if (!annotatedMethods.isEmpty()) {
                for (MethodMetadata method : annotatedMethods) {
                    // 将标注了@EventListener注解的方法添加到事件监听器工厂列表中
                    factories.add(new DefaultEventListenerFactory(applicationContext, beanName, method));
                }
            }
        }
    }
}
  1. 使用监听器工厂创建事件监听器

在DefaultEventListenerFactory类中,会根据Bean名称和目标方法创建一个ApplicationListenerAdapter对象。该对象会封装目标方法并提供给Spring的事件派发器使用。具体实现代码如下:

代码语言:javascript
复制
public ApplicationListener<?> createApplicationListener(final String beanName) {
    // 创建ApplicationListenerAdapter对象,该对象会封装目标方法并提供给Spring的事件派发器使用。
    final Object bean = this.applicationContext.getBean(beanName);
    return new ApplicationListenerAdapter() {
        @Override
        public void onApplicationEvent(final ApplicationEvent event) {
            ReflectionUtils.invokeMethod(method, bean, event);
        }
    };
}
  1. 注册事件到容器中

在EventListenerMethodProcessor类中,实现了SmartInitializingSingleton接口,并且主要负责将@EventListener注解注册为事件监听器。具体来说,它会在afterSingletonsInstantiated()方法中遍历所有BeanDefinition,检查是否存在带有@EventListener注解的方法,如果存在,则将其封装成ApplicationListenerAdapter对象,并将其注册到全局的事件派发器中(ApplicationEventMulticaster)。

代码语言:javascript
复制
@Override
public void afterSingletonsInstantiated() {
    synchronized (this.lifecycleMonitor) {
        for (final Map.Entry<EventListenerFactory, Set<String>> entry : this.eventListenerFactories.entrySet()) {
            EventListenerFactory listenerFactory = entry.getKey();
            Set<String> beanNames = entry.getValue();
            for (String beanName : beanNames) {
                // 将标注了@EventListener注解的方法转换为ApplicationListenerAdapter对象,并注册到全局的事件派发器中。
                addApplicationListener(listenerFactory.createApplicationListener(beanName));
            }
        }
        this.eventListenerFactories.clear();
    }
}
四、总结

Spring框架中的事件机制是基于观察者模式的一种典型应用。该机制提供了一种解耦合的方式,使得系统中不同组件之间可以松散地协作,从而提高了应用的可维护性、可扩展性和可测试性。

在Spring中,事件机制包括三个主要部分:事件、事件源和监听器。事件是发生在特定时刻或条件下的一个动作或状态的改变,例如用户登录、订单生成等;事件源是产生事件的对象,例如用户对象、订单对象等;监听器是根据事件类型来注册的一个或多个回调函数,当事件源发生指定类型的事件时,监听器将被自动触发执行。

事件机制的核心是事件发布和监听过程。在Spring中,事件发布是通过ApplicationEventPublisher接口实现的。开发人员可以将其注入到需要发布事件的Bean中,并调用其publishEvent()方法来发布事件。当事件发布后,Spring框架会自动将事件传递给所有已注册相应事件类型的监听器。

Spring事件机制的优点在于:

  1. 解耦合:事件机制通过定义明确的事件和监听器接口,将事件产生方和事件处理方解耦合,避免了耦合度过高的代码实现。
  2. 扩展性:事件机制可以很容易地新增或修改事件或监听器,不会对系统的其他组件造成影响。
  3. 可维护性:事件机制使应用程序更容易维护。当系统中的某个功能需要扩展或修改时,只需要添加或修改相应的监听器,而不必改变其他组件的实现逻辑。
  4. 可测试性:事件机制可以很方便地进行单元测试和集成测试。开发人员可以编写相应的测试类,模拟事件的发生和处理过程,从而保证系统的正确性。

总之,Spring框架中的事件机制是一种高效、灵活、可扩展的解耦合编程方式,它能够提高应用的可维护性、可扩展性和可测试性,是现代Java应用程序开发中不可或缺的一部分。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-03-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring源码浅析——事件和异步事件
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档