由Spring Cloud Bootstrap上下文和Spring Boot加载了两次ApplicationContextInitializer

时间:2019-01-02 12:46:23

标签: spring-boot spring-cloud

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

  • 一次通过Spring Cloud Boostrap上下文
  • 通过Spring Boot上下文一次

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

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

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

  

检查上下文的ID。

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

  • application用于Spring Cloud Bootstrap上下文
  • application-1用于Spring Boot上下文

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

问题:如何仅一次可靠地加载初始化程序?

如果ApplicationContextInitializer应该是幂等的,则应该在接口的Javadocs中找到它。

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

1 个答案:

答案 0 :(得分:0)

当尝试在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中的应用程序上下文的ID。您还可以使用它来确定初始化程序在哪个上下文中运行。