首页 > 解决方案 > ApplicationContextInitializer 由 Spring Cloud Bootstrap 上下文和 Spring Boot 加载两次

问题描述

spring.factories文件中声明初始化器以创建 Spring Boot 启动器时,我们意识到这些初始化器被加载了两次:

在我们的例子中,我们在 docker 容器中启动数据库,所以我们不想做两次。

根据这个问题,这是 Spring Cloud 的预期行为:https ://github.com/spring-cloud/spring-cloud-config/issues/1151

当询问如何将 boostrap 上下文与“常规”应用程序上下文区分开来时,给出的答案是

检查上下文的 ID。

运行示例应用程序后,ConfigurableApplicationContext.getId()默认返回:

我们的一些用户没有定义spring.application.name,其他用户根本不使用 Spring Cloud。

问题:我们如何才能可靠地只加载一次初始化程序?

如果ApplicationContextInitializers 是幂等的,它可能应该出现在接口的 Javadocs 中。

在最坏的情况下,我们如何安全地区分 Spring Cloud boostrap 上下文和 Spring Boot 上下文?

标签: spring-bootspring-cloud

解决方案


EnvironmentPostProcessor 尝试在here中注入属性源时遇到了同样的问题。解决方案非常简单,因为您只需要一个静态标志:

public class YourInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>
{
    private static boolean initialized = false;

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext)
    {
        if (!initialized) {
            //do your things here
            initialized = true;
        }
    }
}

引导应用程序上下文将始终在常规 Spring Boot 应用程序上下文之前运行,因此您也可以使用它在正确的位置运行您的代码。

最后,引导上下文在BootstrapApplicationListener. 从那里,您可以看到该spring.application.name属性被设置为spring.cloud.bootstrap.namebootstrap作为后备的值。然后将其设置为ContextIdApplicationContextInitializer. 您还可以使用它来确定您的初始化程序在哪个上下文中运行。


推荐阅读