Spring Boot - Application Events 的发布顺序_ContextRefreshedListener

文章目录


Pre

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent


概述

Spring Boot 的广播机制是基于观察者模式实现的,它允许在 Spring 应用程序中发布和监听事件。这种机制的主要目的是为了实现解耦,使得应用程序中的不同组件可以独立地改变和复用逻辑,而无需直接进行通信。

在 Spring Boot 中,事件发布和监听的机制是通过 ApplicationEventApplicationListener 以及事件发布者(ApplicationEventPublisher)来实现的。其中,ApplicationEvent 是所有自定义事件的基础,自定义事件需要继承自它。

ApplicationListener 是监听特定事件并做出响应的接口,开发者可以通过实现该接口来定义自己的监听器。事件发布者(通常由 Spring 的 ApplicationContext 担任)负责发布事件。



Code

java 复制代码
package com.artisan.event;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {

    /**
     * ContextRefreshedEvent 在应用程序上下文初始化过程完成时触发。
     * 在此阶段,所有 Bean 都已在上下文中定义,应用程序已准备好处理请求和操作
     * <p>
     * <p>
     * 我们可以利用 ContextRefreshedEvent 来执行在应用程序上下文完全初始化后需要执行的其他设置或初始化任务。
     * 例如,我们可以启动后台任务、安排作业、建立与外部系统的连接或执行任何其他初始化后逻辑。
     * <p>
     * 为了处理 ContextRefreshedEvent ,我们可以通过实现 ContextRefreshedEvent
     * 作为泛型类型的 ApplicationListener 接口来创建一个自定义事件侦听器。
     * 此侦听器可以在主应用程序类中手动注册
     *
     * @param event the event to respond to
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("--------------------> Handling ContextRefreshedEvent here!");
    }
}
    
    

如何使用呢?

方式一:

java 复制代码
@SpringBootApplication
public class LifeCycleApplication {

    /**
     * 除了手工add , 在 META-INF下面 的 spring.factories 里增加
     * org.springframework.context.ApplicationListener=自定义的listener 也可以
     *
     * @param args
     */
    public static void main(String[] args) {
      
        SpringApplication springApplication = new SpringApplication(LifeCycleApplication.class);

        // 当我们运行 Spring Boot 应用程序时,将调用 的方法 ContextRefreshedListener , onApplicationEvent() 允许我们在应用程序上下文完全初始化后执行操作或执行逻辑
        springApplication.addListeners(new ContextRefreshedListener());

       springApplication.run(args);
    }


}

方式二: 通过spring.factories 配置

xml 复制代码
org.springframework.context.ApplicationListener=\
com.artisan.event.ContextRefreshedListener

运行日志


源码分析

首先main方法启动入口

java 复制代码
SpringApplication.run(LifeCycleApplication.class, args);

跟进去

java 复制代码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

继续

java 复制代码
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

这里首先关注 new SpringApplication(primarySources)

new SpringApplication(primarySources)

java 复制代码
	/**
	 * Create a new {@link SpringApplication} instance. The application context will load
	 * beans from the specified primary sources (see {@link SpringApplication class-level}
	 * documentation for details. The instance can be customized before calling
	 * {@link #run(String...)}.
	 * @param resourceLoader the resource loader to use
	 * @param primarySources the primary bean sources
	 * @see #run(Class, String[])
	 * @see #setSources(Set)
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

聚焦 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));


run

继续run

java 复制代码
// 开始启动Spring应用程序
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch(); // 创建一个计时器
    stopWatch.start(); // 开始计时
    
    DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // 创建引导上下文
    ConfigurableApplicationContext context = null; // Spring应用上下文,初始化为null
    
    configureHeadlessProperty(); // 配置无头属性(如:是否在浏览器中运行)

    SpringApplicationRunListeners listeners = getRunListeners(args); // 获取运行监听器
    listeners.starting(bootstrapContext, this.mainApplicationClass); // 通知监听器启动过程开始
    
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 创建应用参数
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 预备环境
        configureIgnoreBeanInfo(environment); // 配置忽略BeanInfo
        
        Banner printedBanner = printBanner(environment); // 打印Banner
        context = createApplicationContext(); // 创建应用上下文
        context.setApplicationStartup(this.applicationStartup); // 设置应用启动状态
        
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 准备上下文
        refreshContext(context); // 刷新上下文,执行Bean的生命周期
        afterRefresh(context, applicationArguments); // 刷新后的操作
        
        stopWatch.stop(); // 停止计时
        if (this.logStartupInfo) { // 如果需要记录启动信息
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); // 记录启动信息
        }
        listeners.started(context); // 通知监听器启动完成
        
        callRunners(context, applicationArguments); // 调用Runner
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, listeners); // 处理运行失败
        throw new IllegalStateException(ex); // 抛出异常
    }

    try {
        listeners.running(context); // 通知监听器运行中
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, null); // 处理运行失败
        throw new IllegalStateException(ex); // 抛出异常
    }
    return context; // 返回应用上下文
}

我们重点看

java 复制代码
refreshContext(context); // 刷新上下文,执行Bean的生命周期

继续跟进就到了熟悉的SpringFramework的代码了

java 复制代码
@Override
public void refresh() throws BeansException, IllegalStateException {
    // 使用startupShutdownMonitor监视器来同步刷新过程,保证线程安全
    synchronized (this.startupShutdownMonitor) {
        // 记录启动步骤,用于监控和管理启动过程
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // 准备刷新上下文环境
        prepareRefresh();

        // 获取一个bean工厂,用于生产bean
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 准备bean工厂,使其适用于当前的上下文环境
        prepareBeanFactory(beanFactory);

        try {
            // 允许子类在bean工厂后处理之前自定义操作
            postProcessBeanFactory(beanFactory);

            // 开始记录bean后处理步骤
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // 调用在上下文中注册为bean的工厂后处理器
            invokeBeanFactoryPostProcessors(beanFactory);

            // 注册拦截bean创建的bean后处理器
            registerBeanPostProcessors(beanFactory);
            // 结束bean后处理步骤的记录
            beanPostProcess.end();

            // 初始化消息源,用于国际化支持
            initMessageSource();

            // 初始化应用事件多播器
            initApplicationEventMulticaster();

            // 在特定的上下文子类中初始化其他特殊bean
            onRefresh();

            // 检查监听器bean并注册它们
            registerListeners();

            // 实例化所有剩余的非懒加载单例bean
            finishBeanFactoryInitialization(beanFactory);

            // 最后一步:发布相应的事件来表示刷新完成
            finishRefresh();
        }

        catch (BeansException ex) {
            // 如果日志记录器开启了警告功能,则记录异常信息
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // 销毁已经创建的单例bean以避免资源悬挂
            destroyBeans();

            // 重置'active'标志
            cancelRefresh(ex);

            // 将异常传播给调用者
            throw ex;
        }

        finally {
            // 重置Spring核心中的公共缓存,因为可能不再需要单例bean的元数据...
            resetCommonCaches();
            // 结束上下文刷新步骤的记录
            contextRefresh.end();
        }
    }
}

finishRefresh();

java 复制代码
protected void finishRefresh() {
    // 清除上下文级别的资源缓存(例如,通过扫描获取的ASM元数据)
    clearResourceCaches();

    // 初始化当前上下文的生命周期处理器
    initLifecycleProcessor();

    //首先将刷新操作传播给生命周期处理器
    getLifecycleProcessor().onRefresh();

    // 发布最终的刷新事件
    publishEvent(new ContextRefreshedEvent(this));

    // 如果不是在原生镜像中运行,则参与LiveBeansView MBean
    if (!IN_NATIVE_IMAGE) {
        LiveBeansView.registerApplicationContext(this);
    }
}
java 复制代码
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    // 断言事件不为null
    Assert.notNull(event, "Event must not be null");

    // 如果事件是ApplicationEvent的实例,则直接使用,否则创建一个新的PayloadApplicationEvent
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        // 如果eventType为null,则使用PayloadApplicationEvent的ResolvableType
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
        }
    }

    // 如果早期应用事件列表不为null,则立即添加到列表中,否则使用应用事件多播器进行发布
    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);
        }
    }
}

请关注: getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

继续

java 复制代码
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    // 如果eventType不为null,则直接使用它;否则,使用resolveDefaultEventType方法来解析事件的默认类型。
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    
    // 获取一个线程池执行器,它用于异步执行监听器调用。
    Executor executor = getTaskExecutor();
    
    // 获取所有对应该事件类型的监听器。
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 如果执行器不为null,则使用它来异步执行监听器调用;
        // 否则,直接同步调用监听器。
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

继续

java 复制代码
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("--------------------> Handling ContextRefreshedEvent here!");
    }
相关推荐
AskHarries2 小时前
Spring Cloud OpenFeign快速入门demo
spring boot·后端
isolusion3 小时前
Springboot的创建方式
java·spring boot·后端
Yvemil73 小时前
《开启微服务之旅:Spring Boot Web开发举例》(一)
前端·spring boot·微服务
星河梦瑾5 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
计算机学长felix6 小时前
基于SpringBoot的“交流互动系统”的设计与实现(源码+数据库+文档+PPT)
spring boot·毕业设计
.生产的驴6 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
顽疲6 小时前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端
撒呼呼6 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot
因我你好久不见6 小时前
springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率
java·spring boot·ffmpeg
Yvemil77 小时前
《开启微服务之旅:Spring Boot Web开发》(二)
前端·spring boot·微服务