当有多个由spring.profiles.active设置的Spring环境配置文件时,优先顺序是什么?

72
我想知道当指定了多个Spring活动配置文件时,它们的优先顺序是什么。
假设我希望激活默认配置文件,但是在存在多个相同元素(例如bean)可以选择但使用不同配置文件时,使用开发配置文件覆盖默认配置文件...
例如,我有两个 PropertySourcesPlaceholderConfigurer bean 配置为环境配置文件中 "default" 和 "dev" 值。
如果我使用以下配置文件激活:-Dspring.profiles.active="default,dev"
那么 "dev" 配置文件会覆盖 "default" 配置文件吗?
如果不能,如何实现上述行为?

1
据我所知,配置文件之间唯一定义的“优先级”是基于bean的声明顺序(就像覆盖任何bean的定义一样),最后一个特定于配置文件的bean会胜出。因此,如果在两个配置文件中都有一个id="dataSource"的bean,则该bean的最后一个定义将被使用,因为第一个定义将被最后一个覆盖。spring.profiles.active中配置文件的顺序实际上并不重要。 - superEb
4个回答

39

spring.profiles.active系统属性中配置文件的顺序不重要。"优先级"是由bean的声明顺序定义的,包括特定于配置文件的bean,最后一个bean定义获胜

采用您的示例,如果使用-Dspring.profiles.active="default,dev",则在此处将使用default配置文件中的props bean,因为它是该bean的最后一个活动定义:

<beans profile="dev">
    <bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:META-INF/dev.properties"/>
    </bean>
</beans>
<beans profile="default">
    <bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:META-INF/default.properties"/>
    </bean>
</beans>

将 bean 的顺序翻转,然后无论如何在 spring.profiles.active 中的配置顺序如何,都会使用 dev 版本。

请注意,我没有使用 <context:property-placeholder/>,因为它不允许您明确指定 bean id,所以我不确定如果使用多个后它会表现出什么行为。我想象中,属性将被合并,这样两者都定义的属性将使用最后一个定义,但每个文件特定的属性将保持不变。

否则,在我的经验中,您通常会按以下顺序定义 bean:

  1. “默认”bean 定义,不特定于某个配置文件
  2. 覆盖环境特定配置文件中的 bean 定义
  3. 覆盖测试特定配置文件中的 bean 定义

这样,如果与其他配置文件一起使用,则测试配置文件中的 bean 将获胜;否则,根据配置文件使用环境特定的bean或默认bean。


1
它如何与类路径扫描一起工作?我猜它不是确定性的,但如果这个或另一个答案能更新这个问题,那就太好了。 - musiKk
当然,有很多方法,但问题仍然存在。有时创建不同的配置文件并不容易。最近我们遇到了这样的情况:test,local, test,integration, local, integration, live。我用属性来解决它,而不是使用配置文件。 - musiKk
2
@superEb 根据Spring Boot文档所述,顺序很重要。 - Prakash K
1
@Prakash,我也对顺序有疑问。我有两个不同的配置文件,分别是stage和stage-qa。如果某个属性(键)在这两个文件中都有定义,那么我定义Spring活动配置文件的方式就很重要了。如果使用-Dspring.active.profiles=stage,stage-qa,则stage-qa会优先生效;如果反过来,则根据顺序进行。你能帮我理解这种行为吗? - Rahul Singh
1
在第2.3.3个配置文件章节中写道:“如果指定了多个配置文件,则采用最后一个为准的策略。” - Betlista
显示剩余4条评论

34

后者覆盖前者。 我牢记在心,但是:

需要记住的一点是,如果您在jar资源中有一些应用程序属性的默认内容,则此资源内容将覆盖来自较不重要配置文件(其他在spring.profiles.active中先定义的配置文件)的外部内容。

示例配置文件: spring.profiles.active=p1,p2,p3

Jar资源中的文件: application-p1.propertiesapplication-p3.properties

外部文件: application-p1.propertiesapplication-p2.properties

最终顺序为(后者覆盖前者):

  1. 资源 application.properties
  2. 外部 application.properties
  3. 资源 application-p1.properties
  4. 外部 application-p1.properties
  5. 外部 application-p2.properties
  6. 资源 application-p3.properties - 这里的诀窍!它���使用p3资源版本中的值覆盖在外部文件中为p1和p2定义的属性。
  7. 外部 application-p3.properties

因此,请记住后者覆盖前者,但也要记住资源在外部之前


1
这个有文档记录吗?我之前问过类似的问题 - https://stackoverflow.com/questions/60333056/property-resolving-for-multiple-spring-profiles-yaml-configuration 但是没有得到答案(而且最近被关闭了)。我知道,当我提供 p1,p2 时,它目前是按照顺序进行的,我能依赖这一点吗? - Betlista
6
在第2.3.3节配置文件中写道:“如果指定了多个配置文件,将使用后面的覆盖前面的策略。” - Betlista
我已经检查过了。Last-wins 已经有文档记录了。我的帖子与当最后一个获胜的配置文件覆盖其他配置文件外部设置但使用获胜配置文件资源内容的情况有关。顺便说一下,Betlista,感谢你提供的参考链接。 - Marek Podyma

13

我必须进行实验来说服自己。

Spring Initializr创建最简单的Spring Boot应用程序。

然后向资源目录添加3个属性文件(第一个已经存在,但是为空)。

# application.properties
foo=foo in application.properties
bar=bar in application.properties
baz=baz in application.properties

# application-foobar.properties
foo=foo in foobar override properties
bar=bar in foobar override properties

# application-barbaz.properties
bar=bar in barbaz override properties
baz=bar in barbaz override properties

然后我添加了这个@Config类以在启动时运行:

package com.example.profilesexperiment;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
class StartupConfig {

    @Autowired
    private Environment environment;

    @Value("${foo}")
    private String foo;

    @Value("${bar}")
    private String bar;

    @Value("${baz}")
    private String baz;

    @Bean
    CommandLineRunner startup() {
        return args -> {
            System.err.println("Active profiles: " + String.join(", ", environment.getActiveProfiles()));
            System.err.println("Foo = " + foo);
            System.err.println("Bar = " + bar);
            System.err.println("Baz = " + baz);
        };
    }
}

随后我使用不同组合的配置文件运行了它。你可以自己尝试,但以下是一些结果:

只有foobar

java -Dspring.profiles.active=foobar -jar target/profiles-experiment-0.0.1-SNAPSHOT.jar   
Active profiles: foobar
Foo = foo in foobar override properties
Bar = bar in foobar override properties
Baz = baz in application.properties

foobar then barbaz

java -Dspring.profiles.active=foobar,barbaz -jar ...
Active profiles: foobar, barbaz
Foo = foo in foobar override properties
Bar = bar in barbaz override properties
Baz = bar in barbaz override properties

barbaz 然后 foobar

java -Dspring.profiles.active=barbaz,foobar -jar ...
Active profiles: barbaz, foobar
Foo = foo in foobar override properties
Bar = bar in foobar override properties
Baz = bar in barbaz override properties

结论:显然,最后一个胜出!

还有一个小细节:未被覆盖的属性会合并成一个大的属性集合(这也是我来搜索这里的原因)。


8

superEB是正确的,对于bean来说,配置文件中的profile顺序并不重要,声明的顺序更加重要。但是请记住,如果你使用基于profile的配置文件,则顺序是很重要的!


这是否意味着,如果我使用此处描述的方法 https://dev59.com/fanka4cB1Zd3GeqPIRSa#48809611,并且我想选择多个配置,我将无法实现?因为最后一个会覆盖之前的吗?还是它们会像独立的配置类一样工作?(假设我将每个配置标记为单独的配置文件) - tryingToLearn
3
如果使用基于配置文件的配置,并且多个每个配置文件定义了相同的属性,那么优先顺序是什么? - Noel Yap
2
据我所知,顺序并未定义,但为了更好地解决问题,请提出一个专门的 Stack Overflow 问题,这样 Spring 团队如果没有其他好的答案也可以回答。 - Patrick Cornelissen
1
在第2.3.3个配置文件章节中写道:“如果指定了多个配置文件,将采用最后一个为准的策略。” - Betlista

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接