Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent

这篇具有很好参考价值的文章主要介绍了Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent,【Spring Boot2.X】,spring boot,事件发布

Pre

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent


概述

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

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

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


ApplicationContextInitializedEvent 是Spring框架中的一个事件,它在Spring应用上下文(ApplicationContext)初始化完成,但还未启动时触发。这个事件是在Spring框架初始化过程中,ApplicationContext对象创建完成,但还未开始加载 beans 和 配置之前发布的。

在Spring框架中,ApplicationContext是核心接口,负责实例化、配置和组装Bean。ApplicationContextInitializedEvent事件可以被用于执行一些需要在Spring应用上下文完全初始化,但是Bean尚未加载时的初始化代码。

使用场景举例:

  1. 自定义初始化逻辑
    当需要在Spring应用上下文初始化后,但Bean加载之前执行特定逻辑时,比如设置共享资源、初始化配置信息等。

  2. 监听应用上下文初始化完成
    用于在Spring应用上下文完全初始化后立即执行某些操作,比如日志记录、系统参数配置等。

  3. 插件或扩展点
    对于需要扩展Spring框架功能的第三方插件,可以在监听到这个事件时,进行一些自定义操作,如添加额外的Bean定义,或者修改已有的Bean定义。

  4. 资源加载与配置
    如果需要在Spring上下文初始化后,但Bean创建之前加载某些资源(如数据库连接、外部配置文件等),这个事件可以提供这样的机会。

在实际使用中,可以通过实现ApplicationListener接口来监听ApplicationContextInitializedEvent事件。

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextInitializedEvent;
public class CustomInitializationListener implements ApplicationListener<ApplicationContextInitializedEvent> {
    @Override
    public void onApplicationEvent(ApplicationContextInitializedEvent event) {
        // 执行初始化逻辑
    }
}

然后,需要在Spring配置文件中注册这个监听器,或者使用注解@Component进行自动注册。

需要注意的是,在Spring Boot项目中,事件监听通常更加自动化,并且通常不需要手动注册监听器。Spring Boot会自动配置并注册事件监听器,开发者只需关注事件的处理逻辑即可。


Code

package com.artisan.event;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Configuration
public class ApplicationContextNewInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    /**
     * ApplicationContextInitializedEvent 在准备应用程序上下文期间,但在将 Bean 定义加载到 Spring 容器之前。
     * <p>
     * 此事件提供了在初始化 Bean 之前执行任务的机会,例如注册属性源和基于上下文环境激活 Bean 等。
     * <p>
     * <p>
     * 为了处理该 ApplicationContextInitializedEvent 事件,
     * 我们可以通过实现 ApplicationContextInitializer ConfigurableApplicationContext 作为泛型类型的接口来为应用程序创建一个额外的初始值设定项。
     * 可以在主应用程序类中手动添加此初始值设定项。
     * <p>
     * <p>
     * 当我们运行 Spring Boot 应用程序时, ApplicationContextNewInitializer 将调用 这将允许我们在加载任何 Bean 定义之前根据需要执行任务
     * new SpringApplicationBuilder(EventsApplication.class).initializers(new ApplicationContextNewInitializer()).run(args);
     *
     * @param applicationContext the application to configure
     */

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("--------------------> Handling ApplicationContextInitializedEvent here!");
    }
}
    
    

如何使用呢?

方式一:

@SpringBootApplication
public class LifeCycleApplication {

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


}

方式二: 通过spring.factories 配置

Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent,【Spring Boot2.X】,spring boot,事件发布

org.springframework.context.ApplicationContextInitializer=\
com.artisan.event.ApplicationContextNewInitializer

运行日志

Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent,【Spring Boot2.X】,spring boot,事件发布


源码分析

首先main方法启动入口

SpringApplication.run(LifeCycleApplication.class, args);

跟进去

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

继续

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

这里首先关注 new SpringApplication(primarySources)

new SpringApplication(primarySources)

	/**
	 * 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

// 开始启动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; // 返回应用上下文
}

我们重点看

   prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

继续

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    // 将环境变量设置到Spring上下文
    context.setEnvironment(environment);
    // 对Spring上下文进行后处理
    postProcessApplicationContext(context);
    // 应用初始izers,这些是对Spring上下文进行额外配置的组件
    applyInitializers(context);
    // 通知监听器,上下文已准备好
    listeners.contextPrepared(context);
    // 关闭bootstrap上下文
    bootstrapContext.close(context);
    // 如果需要记录启动信息
    if (this.logStartupInfo) {
        // 记录启动信息,并判断是否为根上下文
        logStartupInfo(context.getParent() == null);
        // 记录Spring Boot的配置信息
        logStartupProfileInfo(context);
    }
    // 注册Spring Boot特定的单例bean
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 注册应用启动参数为单例bean,键为'springApplicationArguments'
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    // 如果有打印的Banner,将其注册为单例bean,键为'springBootBanner'
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    // 如果bean工厂是DefaultListableBeanFactory的实例,设置是否允许Bean定义覆盖
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // 如果设置了懒惰初始化,添加一个后处理器
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // 加载所有源,通常是Bean定义的来源
    Set<Object> sources = getAllSources();
    // 断言源集合不为空,这些源将被加载到Spring上下文中
    Assert.notEmpty(sources, "Sources must not be empty");
    // 使用源数组加载Spring上下文
    load(context, sources.toArray(new Object[0]));
    // 通知监听器,上下文已加载
    listeners.contextLoaded(context);
}


【applyInitializers】

		protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}

Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent,【Spring Boot2.X】,spring boot,事件发布

就到了我们自定义实现的代码逻辑中了。

     @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("--------------------> Handling ApplicationContextInitializedEvent here!");
    }

继续

listeners.contextPrepared(context);	

又看了熟悉的

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

继续

	@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}
@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);
        }
    }
}

继续

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			......
		}
	}

Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent,【Spring Boot2.X】,spring boot,事件发布文章来源地址https://www.toymoban.com/news/detail-795768.html

到了这里,关于Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot - Application Events 同步 VS 异步 发布订阅事件实战

    Spring Boot - Application Events 的发布顺序_ApplicationStartingEvent Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent Spring Boot - Application Events 的发布顺序_ApplicationPreparedEvent Spring Boot - Application Events 的发布顺

    2024年01月17日
    浏览(79)
  • 聊聊Spring Boot配置文件:优先级顺序、加载顺序、bootstrap.yml与application.yml区别详解

    在 Spring Boot 中,配置文件的优先级顺序是: application-{profile}.yml ( application-{profile}.properties ) application.yml ( application.properties ) bootstrap.yml ( bootstrap.properties )。其中, {profile} 表示不同的环境配置,如 dev 、 test 、 prod 等。 优先级从高到低,高优先级的配置覆盖低优先级

    2024年01月25日
    浏览(59)
  • 【SpringBoot笔记34】Spring Events事件驱动编程之事件的发布和监听操作

    这篇文章,主要介绍Spring Events事件驱动编程之事件的发布和监听操作。 目录 一、基于接口实现 1.1、自定义事件 1.2、主动发布事件 1.3、监听事件对象

    2024年02月16日
    浏览(39)
  • Spring Boot中使用Server-Sent Events (SSE) 实现实时数据推送教程

    Server-Sent Events (SSE) 是HTML5引入的一种轻量级的服务器向浏览器客户端单向推送实时数据的技术。在Spring Boot框架中,我们可以很容易地集成并利用SSE来实现实时通信。          在Spring Boot项目中,无需额外引入特定的依赖,因为Spring Web MVC模块已经内置了对SSE的支持。 辅助

    2024年03月18日
    浏览(59)
  • 【Spring Boot】application 配置文件(2)

    server.servlet.session. timeout =1800  定义了 HTTP 会话的超时时间,单位是秒,在此设置中,会话的超时时间被设置为 1800秒 即30分钟。这意味着 ,如果30分钟内用户没有与服务器进行任何活动(如请求页面),那么会话将自动过期。  spring.jackson.time-zone=GMT+8 这个设置指定了jackson 库

    2024年01月23日
    浏览(43)
  • Spring Boot Application.properties和yaml配置文件

    全局配置文件能够对一些默认配置值进行修改。Spring Boot使用一个application.properties或者application.yaml的文件作为全局配置文件,该文件存放在src/main/resource目录或者类路径的/config,一般会选择resource目录。 使用Spring Initializr方式创建项目——PropertiesDemo 单击【Next】按钮 添加W

    2024年01月24日
    浏览(69)
  • Java实战:Spring Boot application.yml配置文件详解

    本文将详细介绍Spring Boot application.yml 配置文件的使用和配置项。我们将探讨 application.yml 文件的基本概念,以及如何使用它来配置Spring Boot应用程序的各个方面。此外,我们将通过具体的示例来展示如何配置不同的Spring Boot组件,如数据源、数据库、缓存、邮件服务等。本文适

    2024年04月24日
    浏览(42)
  • 【Spring Boot基础】解决ERROR org.springframework.boot.SpringApplication -- Application run failed报错问题

    一、问题描述 在学习SpringBoot配置profile的时候,.yml文件内容明明跟老师敲的一模一样,但还是报错ERROR org.springframework.boot.SpringApplication -- Application run failed 原.yml文件内容: 报错如下:  二、解决方法 按照“ERROR org.springframework.boot.SpringApplication -- Application run failed”搜索答案

    2024年02月13日
    浏览(60)
  • Spring Boot学习随笔-第一个SpringBoot项目快速启动(org.springframework.boot、@SpringBootApplication、application.yml)

    学习视频:【编程不良人】2021年SpringBoot最新最全教程 创建第一个Module 环境要求 jdk1.8+ maven3.2+ Spring Framework 5.x+ Tomcat 9.0+ IDEA 2021 自动保存刷新pom 在resources下添加application.yml文件后,即可启动springboot应用 由于tomcat内嵌在springboot里面了,所以我们在修改端口号等设置也在配置

    2024年02月05日
    浏览(56)
  • spring-boot项目启动类错误: 找不到或无法加载主类 com.**Application

    问题:Springboot项目启动报错:错误: 找不到或无法加载主类 com.**Application 解决步骤: 1.File–Project Structure 2.Modules–选中你的项目–点击“-”移除 3.重新导入:点击“+”号,选择Import Module,找到项目所在路径,选中要导入的项目,点击OK。 选择maven,点击ok 4.重新编译:点击

    2024年04月17日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包