Java Spring Boot 和多个外部配置文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25855795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 01:19:21  来源:igfitidea点击:

Spring Boot and multiple external configuration files

javaspringconfigspring-boot

提问by nir

I have multiple property files that I want to load from classpath. There is one default set under /src/main/resourceswhich is part of myapp.jar. My springcontextexpects files to be on the classpath. i.e.

我有多个要从类路径加载的属性文件。有一个默认设置,/src/main/resources它是myapp.jar. 我springcontext希望文件在类路径上。IE

<util:properties id="Job1Props"
    location="classpath:job1.properties"></util:properties>

<util:properties id="Job2Props"
    location="classpath:job2.properties"></util:properties>

I also need the option to override these properties with an external set. I have an external config folder in cwd. As per spring boot doc config folder should be on classpath. But its not clear from doc if it will only override the applicaiton.propertiesfrom there or all the properties in config.

我还需要使用外部集覆盖这些属性的选项。我在cwd. 根据 spring boot doc config 文件夹应该在类路径上。但是从文档中不清楚它是否只会覆盖applicaiton.properties从那里或配置中的所有属性。

When I tested it, only application.propertiesgets picked up and rest of properties are still picked up from /src/main/resources. I have tried supplying them as comma separated list to spring.config.locationbut the default set is still not being overriden.

当我测试它时,只有application.properties被拾取,其余的属性仍然从/src/main/resources. 我尝试将它们作为逗号分隔列表提供给,spring.config.location但默认设置仍未被覆盖。

How do I make mulitiple external config files override default ones?

如何使多个外部配置文件覆盖默认配置文件?

As workaround I currently used app.config.location(app specific property) which I supply through the command line. i.e

作为我目前使用的解决方法app.config.location(应用程序特定属性),我通过命令行提供。IE

java -jar myapp.jar app.config.location=file:./config

and I changed my applicationcontextto

我改变了我applicationcontext

<util:properties id="Job2Props"
    location="{app.config.location}/job2.properties"></util:properties>

And this is how I make separation between file and classpath while loading Application.
EDITS:

这就是我在加载应用程序时分离文件和类路径的方式。
编辑:

//psuedo code

if (StringUtils.isBlank(app.config.location)) {
            System.setProperty(APP_CONFIG_LOCATION, "classpath:");
}

I would really like not to use the above workaround and have spring override all external config files on the classpath like it does for the application.propertiesfile.

我真的不想使用上述解决方法,而是让 spring 覆盖类路径上的所有外部配置文件,就像它对application.properties文件所做的那样。

回答by M. Deinum

When using Spring Boot the properties are loaded in the following order (see Externalized Configurationin the Spring Boot reference guide).

使用 Spring Boot 时,属性按以下顺序加载(请参阅Spring Boot 参考指南中的外部化配置)。

  1. Command line arguments.
  2. Java System properties (System.getProperties()).
  3. OS environment variables.
  4. JNDI attributes from java:comp/env
  5. A RandomValuePropertySource that only has properties in random.*.
  6. Application properties outside of your packaged jar (application.properties including YAML and profile variants).
  7. Application properties packaged inside your jar (application.properties including YAML and profile variants).
  8. @PropertySource annotations on your @Configuration classes.
  9. Default properties (specified using SpringApplication.setDefaultProperties).
  1. 命令行参数。
  2. Java 系统属性 (System.getProperties())。
  3. 操作系统环境变量。
  4. 来自 java:comp/env 的 JNDI 属性
  5. 仅在 random.* 中具有属性的 RandomValuePropertySource。
  6. 打包 jar 之外的应用程序属性(application.properties 包括 YAML 和配置文件变体)。
  7. 打包在 jar 中的应用程序属性(application.properties 包括 YAML 和配置文件变体)。
  8. @Configuration 类上的 @PropertySource 注释。
  9. 默认属性(使用 SpringApplication.setDefaultProperties 指定)。

When resolving properties (i.e. @Value("${myprop}")resolving is done in the reverse order (so starting with 9).

当解析属性时(即@Value("${myprop}")以相反的顺序进行解析(所以从 9 开始)。

To add different files you can use the spring.config.locationproperties which takes a comma separated list of property files or file location (directories).

要添加不同的文件,您可以使用spring.config.location以逗号分隔的属性文件列表或文件位置(目录)的属性。

-Dspring.config.location=your/config/dir/

The one above will add a directory which will be consulted for application.propertiesfiles.

上面的将添加一个目录,该目录将用于查询application.properties文件。

-Dspring.config.location=classpath:job1.properties,classpath:job2.properties

This will add the 2 properties file to the files that are loaded.

这会将 2 个属性文件添加到加载的文件中。

The default configuration files and locations are loaded before the additonally specified spring.config.locationones meaning that the latter will always override properties set in the earlier ones. (See also this sectionof the Spring Boot Reference Guide).

默认配置文件和位置在附加指定spring.config.location的文件和位置之前加载,这意味着后者将始终覆盖在较早文件中设置的属性。(另请参阅Spring Boot 参考指南的这一部分)。

If spring.config.locationcontains directories (as opposed to files) they should end in / (and will be appended with the names generated from spring.config.namebefore being loaded). The default search path classpath:,classpath:/config,file:,file:config/is always used, irrespective of the value of spring.config.location. In that way you can set up default values for your application in application.properties(or whatever other basename you choose with spring.config.name) and override it at runtime with a different file, keeping the defaults.

如果spring.config.location包含目录(而不是文件),它们应该以 / 结尾(并且会附加spring.config.name在加载之前生成的名称)。classpath:,classpath:/config,file:,file:config/无论 的值如何,始终使用默认搜索路径spring.config.location。通过这种方式,您可以为您的应用程序application.properties(或您选择的任何其他基本名称spring.config.name)设置默认值,并在运行时使用不同的文件覆盖它,保持默认值。

UPDATE: As the behaviour of spring.config.location now overrides the default instead of adding to it. You need to use spring.config.additional-location to keep the defaults. This is a change in behaviour from 1.x to 2.x

更新:由于 spring.config.location 的行为现在覆盖默认值而不是添加到默认值。您需要使用 spring.config.additional-location 来保持默认值。这是从 1.x 到 2.x 的行为变化

回答by user3206144

Take a look at the PropertyPlaceholderConfigurer, I find it clearer to use than annotation.

看看PropertyPlaceholderConfigurer,我发现它比注释更易于使用。

e.g.

例如

@Configuration
public class PropertiesConfiguration {


    @Bean
    public PropertyPlaceholderConfigurer properties() {
        final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
//        ppc.setIgnoreUnresolvablePlaceholders(true);
        ppc.setIgnoreResourceNotFound(true);

        final List<Resource> resourceLst = new ArrayList<Resource>();

        resourceLst.add(new ClassPathResource("myapp_base.properties"));
        resourceLst.add(new FileSystemResource("/etc/myapp/overriding.propertie"));
        resourceLst.add(new ClassPathResource("myapp_test.properties"));
        resourceLst.add(new ClassPathResource("myapp_developer_overrides.properties")); // for Developer debugging.

        ppc.setLocations(resourceLst.toArray(new Resource[]{}));

        return ppc;
    }

回答by robjwilkins

I've just had a similar problem to this and finally figured out the cause: the application.properties file had the wrong ownership and rwx attributes. So when tomcat started up the application.properties file was in the right location, but owned by another user:

我刚刚遇到了与此类似的问题,并最终找出了原因:application.properties 文件的所有权和 rwx 属性错误。因此,当 tomcat 启动时 application.properties 文件位于正确的位置,但由另一个用户拥有:

$ chmod 766 application.properties

$ chown tomcat application.properties

回答by mxsb

I had the same problem. I wanted to have the ability to overwrite an internal configuration file at startup with an external file, similar to the Spring Boot application.properties detection. In my case it's a user.properties file where my applications users are stored.

我有同样的问题。我希望能够在启动时使用外部文件覆盖内部配置文件,类似于 Spring Boot application.properties 检测。就我而言,它是存储我的应用程序用户的 user.properties 文件。

My requirements:

我的要求:

Load the file from the following locations (in this order)

从以下位置加载文件(按此顺序)

  1. The classpath
  2. A /configsubdir of the current directory.
  3. The current directory
  4. From directory or a file location given by a command line parameter at startup
  1. 类路径
  2. /配置当前目录的子目录。
  3. 当前目录
  4. 来自启动时命令行参数指定的目录或文件位置

I came up with the following solution:

我想出了以下解决方案:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.Properties;

import static java.util.Arrays.stream;

@Configuration
public class PropertiesConfig {

    private static final Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);

    private final static String PROPERTIES_FILENAME = "user.properties";

    @Value("${properties.location:}")
    private String propertiesLocation;

    @Bean
    Properties userProperties() throws IOException {
        final Resource[] possiblePropertiesResources = {
                new ClassPathResource(PROPERTIES_FILENAME),
                new PathResource("config/" + PROPERTIES_FILENAME),
                new PathResource(PROPERTIES_FILENAME),
                new PathResource(getCustomPath())
        };
        // Find the last existing properties location to emulate spring boot application.properties discovery
        final Resource propertiesResource = stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .reduce((previous, current) -> current)
                .get();
        final Properties userProperties = new Properties();

        userProperties.load(propertiesResource.getInputStream());

        LOG.info("Using {} as user resource", propertiesResource);

        return userProperties;
    }

    private String getCustomPath() {
        return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + PROPERTIES_FILENAME;
    }

}

Now the application uses the classpath resource, but checks for a resource at the other given locations too. The last resource which exists will be picked and used. I'm able to start my app with java -jar myapp.jar --properties.location=/directory/myproperties.properties to use an properties location which floats my boat.

现在应用程序使用类路径资源,但也在其他给定位置检查资源。将选择并使用存在的最后一个资源。我可以使用 java -jar myapp.jar --properties.location=/directory/myproperties.properties 启动我的应用程序,以使用浮动我的船的属性位置。

An important detail here: Use an empty String as default value for the properties.location in the @Value annotation to avoid errors when the property is not set.

这里的一个重要细节:在 @Value 注释中使用空字符串作为 properties.location 的默认值,以避免在未设置属性时出错。

The convention for a properties.location is: Use a directory or a path to a properties file as properties.location.

properties.location 的约定是:使用属性文件的目录或路径作为 properties.location。

If you want to override only specific properties, a PropertiesFactoryBean with setIgnoreResourceNotFound(true) can be used with the resource array set as locations.

如果您只想覆盖特定的属性,可以将带有 setIgnoreResourceNotFound(true) 的 PropertiesFactoryBean 与设置为位置的资源数组一起使用。

I'm sure that this solution can be extended to handle multiple files...

我确信这个解决方案可以扩展到处理多个文件......

EDIT

编辑

Here my solution for multiple files :) Like before, this can be combined with a PropertiesFactoryBean.

这是我针对多个文件的解决方案 :) 像以前一样,这可以与 PropertiesFactoryBean 结合使用。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

@Configuration
class PropertiesConfig {

    private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
    private final static String[] PROPERTIES_FILENAMES = {"job1.properties", "job2.properties", "job3.properties"};

    @Value("${properties.location:}")
    private String propertiesLocation;

    @Bean
    Map<String, Properties> myProperties() {
        return stream(PROPERTIES_FILENAMES)
                .collect(toMap(filename -> filename, this::loadProperties));
    }

    private Properties loadProperties(final String filename) {
        final Resource[] possiblePropertiesResources = {
                new ClassPathResource(filename),
                new PathResource("config/" + filename),
                new PathResource(filename),
                new PathResource(getCustomPath(filename))
        };
        final Resource resource = stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .reduce((previous, current) -> current)
                .get();
        final Properties properties = new Properties();

        try {
            properties.load(resource.getInputStream());
        } catch(final IOException exception) {
            throw new RuntimeException(exception);
        }

        LOG.info("Using {} as user resource", resource);

        return properties;
    }

    private String getCustomPath(final String filename) {
        return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + filename;
    }

}

回答by Farzan Skt

this is one simple approach using spring boot

这是使用 spring boot 的一种简单方法

TestClass.java

测试类.java

@Configuration
@Profile("one")
@PropertySource("file:/{selected location}/app.properties")
public class TestClass {

    @Autowired
    Environment env;

    @Bean
    public boolean test() {
        System.out.println(env.getProperty("test.one"));
        return true;
    }
}

the app.propertiescontext, in your selected location

app.properties背景下,您选择的位置

test.one = 1234

your spring boot application

您的Spring Boot 应用程序

@SpringBootApplication

public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(testApplication.class, args);
    }
}

and the predefined application.propertiescontext

和预定义的application.properties上下文

spring.profiles.active = one

you can write as many configuration class as you like and enable/disable them just by setting spring.profiles.active= the profile name/names {separated by commas}

您可以根据需要编写任意数量的配置类,并通过设置spring.profiles.active= 配置文件名称/名称{以逗号分隔}来启用/禁用它们

as you can see spring boot is great it just needs sometime to get familiar with, it's worth mentioning that you may use @Value on your fields as well

正如您所看到的,spring boot 很棒,只是需要一些时间来熟悉,值得一提的是,您也可以在字段上使用 @Value

@Value("${test.one}")
String str;

回答by ganesh jadhav

With Spring boot , the spring.config.location does work,just provide comma separated properties files.

使用 Spring boot , spring.config.location 确实有效,只需提供逗号分隔的属性文件。

see the below code

看下面的代码

@PropertySource(ignoreResourceNotFound=true,value="classpath:jdbc-${spring.profiles.active}.properties")
public class DBConfig{

     @Value("${jdbc.host}")
        private String jdbcHostName;
     }
}

one can put the default version of jdbc.properties inside application. The external versions can be set lie this.

可以将 jdbc.properties 的默认版本放在应用程序中。可以设置外部版本。

java -jar target/myapp.jar --spring.config.location=classpath:file:///C:/Apps/springtest/jdbc.properties,classpath:file:///C:/Apps/springtest/jdbc-dev.properties

Based on profile value set using spring.profiles.active property, the value of jdbc.host will be picked up. So when (on windows)

根据使用 spring.profiles.active 属性设置的配置文件值,将选取 jdbc.host 的值。所以当(在窗户上)

set spring.profiles.active=dev

jdbc.host will take value from jdbc-dev.properties.

jdbc.host 将从 jdbc-dev.properties 中获取值。

for

为了

set spring.profiles.active=default

jdbc.host will take value from jdbc.properties.

jdbc.host 将从 jdbc.properties 中获取值。

回答by Humble Freak

spring boot allows us to write different profiles to write for different environments, for example we can have separate properties files for production, qa and local environments

spring boot 允许我们为不同的环境编写不同的配置文件,例如我们可以为生产、qa 和本地环境拥有单独的属性文件

application-local.properties file with configurations according to my local machine is

根据我的本地机器配置的 application-local.properties 文件是

spring.profiles.active=local

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=users
spring.data.mongodb.username=humble_freak
spring.data.mongodb.password=freakone

spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672

rabbitmq.publish=true

Similarly, we can write application-prod.properties and application-qa.properties as many properties files as we want

类似地,我们可以根据需要编写 application-prod.properties 和 application-qa.properties 任意数量的属性文件

then write some scripts to start the application for different environments, for e.g.

然后编写一些脚本来启动不同环境的应用程序,例如

mvn spring-boot:run -Drun.profiles=local
mvn spring-boot:run -Drun.profiles=qa
mvn spring-boot:run -Drun.profiles=prod

回答by davidfrancis

I have found this to be a useful pattern to follow:

我发现这是一个有用的模式:

@RunWith(SpringRunner)
@SpringBootTest(classes = [ TestConfiguration, MyApplication ],
        properties = [
                "spring.config.name=application-MyTest_LowerImportance,application-MyTest_MostImportant"
                ,"debug=true", "trace=true"
        ]
)

Here we override the use of "application.yml" to use "application-MyTest_LowerImportance.yml" and also "application-MyTest_MostImportant.yml"
(Spring will also look for .properties files)

在这里,我们覆盖了“application.yml”的使用,以使用“application-MyTest_LowerImportance.yml”和“application-MyTest_MostImportant.yml”
(Spring 也会寻找 .properties 文件)

Also included as an extra bonus are the debug and trace settings, on a separate line so you can comment them out if required ;]

还包括作为额外奖励的调试和跟踪设置,在单独的一行中,以便您可以在需要时将它们注释掉;]

The debug/trace are incredibly useful as Spring will dump the names of all the files it loads and those it tries to load.
You will see lines like this in the console at runtime:

调试/跟踪非常有用,因为 Spring 将转储它加载的所有文件的名称以及它尝试加载的文件的名称。
您将在运行时在控制台中看到这样的行:

TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant.properties' (file:./config/application-MyTest_MostImportant.properties) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant.xml' (file:./config/application-MyTest_MostImportant.xml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant.yml' (file:./config/application-MyTest_MostImportant.yml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant.yaml' (file:./config/application-MyTest_MostImportant.yaml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_LowerImportance.properties' (file:./config/application-MyTest_LowerImportance.properties) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_LowerImportance.xml' (file:./config/application-MyTest_LowerImportance.xml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_LowerImportance.yml' (file:./config/application-MyTest_LowerImportance.yml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_LowerImportance.yaml' (file:./config/application-MyTest_LowerImportance.yaml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_MostImportant.properties' (file:./application-MyTest_MostImportant.properties) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_MostImportant.xml' (file:./application-MyTest_MostImportant.xml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_MostImportant.yml' (file:./application-MyTest_MostImportant.yml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_MostImportant.yaml' (file:./application-MyTest_MostImportant.yaml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_LowerImportance.properties' (file:./application-MyTest_LowerImportance.properties) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_LowerImportance.xml' (file:./application-MyTest_LowerImportance.xml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_LowerImportance.yml' (file:./application-MyTest_LowerImportance.yml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_LowerImportance.yaml' (file:./application-MyTest_LowerImportance.yaml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_MostImportant.properties' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_MostImportant.xml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_MostImportant.yml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_MostImportant.yaml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_LowerImportance.properties' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_LowerImportance.xml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_LowerImportance.yml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_LowerImportance.yaml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_MostImportant.properties' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_MostImportant.xml' resource not found
DEBUG 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Loaded config file 'file:/Users/xxx/dev/myproject/target/test-classes/application-MyTest_MostImportant.yml' (classpath:/application-MyTest_MostImportant.yml)
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_MostImportant.yaml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_LowerImportance.properties' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_LowerImportance.xml' resource not found
DEBUG 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Loaded config file 'file:/Users/xxx/dev/myproject/target/test-classes/application-MyTest_LowerImportance.yml' (classpath:/application-MyTest_LowerImportance.yml)
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_LowerImportance.yaml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant-test.properties' (file:./config/application-MyTest_MostImportant-test.properties) resource not found

回答by davidxxx

Spring boot 1.X and Spring Boot 2.X don't provide the same options and behavior about the Externalized Configuration.

Spring Boot 1.X 和 Spring Boot 2.X 不提供关于Externalized Configuration.

The very good answer of M. Deinum refers to Spring Boot 1 specifities.
I will update for Spring Boot 2 here.

M. Deinum 的很好的回答是指 Spring Boot 1 的特性。
我将在这里更新 Spring Boot 2。

Environment properties sources and order

环境属性来源和顺序

Spring Boot 2 uses a very particular PropertySourceorder that is designed to allow sensible overriding of values. Properties are considered in the following order:

Spring Boot 2 使用了一个非常特殊的PropertySource顺序,旨在允许合理地覆盖值。属性按以下顺序考虑:

  • Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).

  • @TestPropertySourceannotations on your tests.

  • @SpringBootTest#propertiesannotation attribute on your tests. Command line arguments.

  • Properties from SPRING_APPLICATION_JSON(inline JSON embedded in an environment variable or system property).

  • ServletConfiginit parameters.

  • ServletContextinit parameters.

  • JNDI attributes from java:comp/env.

  • Java System properties (System.getProperties()).

  • OS environment variables.

  • A RandomValuePropertySourcethat has properties only in random.*.

  • Profile-specific application properties outside of your packaged jar (application-{profile}.propertiesand YAML variants).

  • Profile-specific application properties packaged inside your jar (application-{profile}.propertiesand YAML variants).

  • Application properties outside of your packaged jar (application.propertiesand YAML variants).

  • Application properties packaged inside your jar (application.propertiesand YAML variants).

  • @PropertySourceannotations on your @Configurationclasses. Default properties (specified by setting SpringApplication.setDefaultProperties).

  • 主目录中的 Devtools 全局设置属性(当 devtools 处于活动状态时为 ~/.spring-boot-devtools.properties)。

  • @TestPropertySource测试中的注释。

  • @SpringBootTest#properties测试中的注释属性。命令行参数。

  • 来自SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的内联 JSON)的属性。

  • ServletConfig初始化参数。

  • ServletContext初始化参数。

  • JNDI 属性来自java:comp/env.

  • Java 系统属性 ( System.getProperties())。

  • 操作系统环境变量。

  • RandomValuePropertySource仅具有随机属性的A。*。

  • 打包的 jar(application-{profile}.properties和 YAML 变体)之外的特定于配置文件的应用程序属性。

  • 打包在 jar 中的特定于配置文件的应用程序属性(application-{profile}.properties和 YAML 变体)。

  • 打包 jar 之外的应用程序属性(application.properties和 YAML 变体)。

  • 打包在 jar 中的应用程序属性(application.properties和 YAML 变体)。

  • @PropertySource你的@Configuration类的注释。默认属性(由 setting 指定 SpringApplication.setDefaultProperties)。

To specify external properties files these options should interest you :

要指定外部属性文件,您应该对这些选项感兴趣:

  • Profile-specific application properties outside of your packaged jar (application-{profile}.propertiesand YAML variants).

  • Application properties outside of your packaged jar (application.propertiesand YAML variants).

  • @PropertySourceannotations on your @Configurationclasses. Default properties (specified by setting SpringApplication.setDefaultProperties).

  • 打包的 jar(application-{profile}.properties和 YAML 变体)之外的特定于配置文件的应用程序属性。

  • 打包 jar 之外的应用程序属性(application.properties和 YAML 变体)。

  • @PropertySource你的@Configuration类的注释。默认属性(由 setting 指定 SpringApplication.setDefaultProperties)。

You can use only one of these 3 options or combine them according to your requirements.
For example for very simple cases using only profile-specific properties is enough but in other cases you may want to use both profile-specific properties, default properties and @PropertySource.

您可以仅使用这 3 个选项之一或根据您的要求组合它们。
例如,对于非常简单的情况,仅使用特定于配置文件的属性就足够了,但在其他情况下,您可能希望同时使用特定于配置文件的属性、默认属性和@PropertySource.

Default locations for application.properties files

application.properties 文件的默认位置

About application.propertiesfiles (and variant), by default Spring loads them and add their properties in the environment from these in the following order :

关于application.properties文件(和变体),默认情况下 Spring 加载它们并按以下顺序在环境中添加它们的属性:

  • A /config subdirectory of the current directory

  • The current directory

  • A classpath /config package

  • The classpath root

  • 当前目录的 /config 子目录

  • 当前目录

  • 一个类路径 /config 包

  • 类路径根

The higher priorities are so literally :
classpath:/,classpath:/config/,file:./,file:./config/.

从字面上看,更高的优先级是:
classpath:/,classpath:/config/,file:./,file:./config/

How to use properties files with specific names ?

如何使用具有特定名称的属性文件?

The default locations are not always enough : the default locations like the default filename (application.properties) may not suit. Besides, as in the OP question you may need to specify multiple configuration files other than application.properties(and variant).
So spring.config.namewill not be enough.

默认位置并不总是足够的:像默认文件名 ( application.properties)这样的默认位置可能不适合。此外,在 OP 问题中,您可能需要指定除application.properties(和变体)之外的多个配置文件。
所以spring.config.name是不够的。

In this case you should provide an explicit location by using the spring.config.locationenvironment property (which is a comma-separated list of directory locations or file paths).
To be free about the filenames pattern favor the list of file paths over the list of directories.
For example do like that :

在这种情况下,您应该使用spring.config.location环境属性(这是一个以逗号分隔的目录位置或文件路径列表)提供一个明确的位置。
为了不受文件名模式的影响,文件路径列表优于目录列表。
例如这样做:

java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

That way is the most verbose that just specifying the folder but it is also the way to specify very finely our configuration files and to document clearly the properties effectively used.

这种方式是最冗长的方式,仅指定文件夹,但它也是非常精细地指定我们的配置文件并清楚地记录有效使用的属性的方式。

spring.config.location now replaces default locations instead of adding to them

spring.config.location 现在替换默认位置而不是添加到它们

With Spring Boot 1, the spring.config.locationargument adds specified locations in the Spring environment.
But from Spring Boot 2, spring.config.locationreplaces the default locations used by Spring by the specified locations in the Spring environment as stated in the documentation.

对于 Spring Boot 1,该spring.config.location参数在 Spring 环境中添加指定位置。
但是从 Spring Boot 2 开始,spring.config.location将Spring使用的默认位置替换为文档中所述的 Spring 环境中的指定位置。

When custom config locations are configured by using spring.config.location, they replace the default locations. For example, if spring.config.locationis configured with the value classpath:/custom-config/,file:./custom-config/, the search order becomes the following:

  1. file:./custom-config/

  2. classpath:custom-config/

使用 配置自定义配置位置时 spring.config.location,它们会替换默认位置。例如,如果spring.config.location配置了值 classpath:/custom-config/, file:./custom-config/,则搜索顺序如下:

  1. file:./custom-config/

  2. classpath:custom-config/

spring.config.locationis now a way to make sure that any application.propertiesfile has to be explicitly specified.
For uber JARs that are not supposed to package application.propertiesfiles, that is rather nice.

spring.config.location现在是一种确保application.properties必须明确指定任何文件的方法。
对于不应该打包application.properties文件的uber JAR ,这是相当不错的。

To keep the old behavior of spring.config.locationwhile using Spring Boot 2 you can use the new spring.config.additional-locationproperty instead of spring.config.locationthat still adds the locations as stated by the documentation:

为了保持spring.config.location使用 Spring Boot 2 时的旧行为,您可以使用新spring.config.additional-location属性而不是spring.config.location仍然添加文档中所述的位置:

Alternatively, when custom config locations are configured by using spring.config.additional-location, they are used in addition to the default locations.

或者,当使用 配置自定义配置位置时spring.config.additional-location,除了默认位置之外,还会使用 它们。



In practice

在实践中

So supposing that as in the OP question, you have 2 external properties file to specify and 1 properties file included in the uber jar.

因此,假设与 OP 问题一样,您有 2 个外部属性文件要指定,并且 uber jar 中包含 1 个属性文件。

To use only configuration files you specified :

要仅使用您指定的配置文件:

-Dspring.config.location=classpath:/job1.properties,classpath:/job2.properties,classpath:/applications.properties   

To add configuration files to these in the default locations :

要将配置文件添加到默认位置:

-Dspring.config.additional-location=classpath:/job1.properties,classpath:/job2.properties

classpath:/applications.propertiesis in the last example not required as the default locations have that and that default locations are here not overwritten but extended.

classpath:/applications.properties在最后一个示例中不需要,因为默认位置具有该位置,并且默认位置在此处不会被覆盖而是被扩展。

回答by Codewarrior

A modified version of @mxsb solution that allows us to define multiple files and in my case these are yml files.

@mxsb 解决方案的修改版本,允许我们定义多个文件,在我的例子中这些是 yml 文件。

In my application-dev.yml, I added this config that allows me to inject all the yml that have -dev.yml in them. This can be a list of specific files also. "classpath:/test/test.yml,classpath:/test2/test.yml"

在我的 application-dev.yml 中,我添加了这个配置,允许我注入所有包含 -dev.yml 的 yml。这也可以是特定文件的列表。“类路径:/test/test.yml,类路径:/test2/test.yml”

application:
  properties:
    locations: "classpath*:/**/*-dev.yml"

This helps to get a properties map.

这有助于获取属性映射。

@Configuration

public class PropertiesConfig {

private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);

@Value("${application.properties.locations}")
private String[] locations;

@Autowired
private ResourceLoader rl;

@Bean
Map<String, Properties> myProperties() {
    return stream(locations)
            .collect(toMap(filename -> filename, this::loadProperties));
}

private Properties loadProperties(final String filename) {

    YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
    try {
        final Resource[] possiblePropertiesResources = ResourcePatternUtils.getResourcePatternResolver(rl).getResources(filename);
        final Properties properties = new Properties();
        stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .map(resource1 -> {
                    try {
                        return loader.load(resource1.getFilename(), resource1);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }).flatMap(l -> l.stream())
                .forEach(propertySource -> {
                    Map source = ((MapPropertySource) propertySource).getSource();
                    properties.putAll(source);
                });

        return properties;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
}

However, if like in my case, I wanted to have to split yml files for each profile and load them and inject that directly into spring configuration before beans initialisation.

但是,如果像我的情况一样,我想必须为每个配置文件拆分 yml 文件并加载它们并在 bean 初始化之前将其直接注入到 spring 配置中。

config
    - application.yml
    - application-dev.yml
    - application-prod.yml
management
    - management-dev.yml
    - management-prod.yml

... you get the idea

......你明白了

The component is slightly different

组件略有不同

@Component
public class PropertiesConfigurer extends     PropertySourcesPlaceholderConfigurer
    implements EnvironmentAware, InitializingBean {

private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfigurer.class);

private String[] locations;

@Autowired
private ResourceLoader rl;
private Environment environment;

@Override
public void setEnvironment(Environment environment) {
    // save off Environment for later use
    this.environment = environment;
    super.setEnvironment(environment);
}

@Override
public void afterPropertiesSet() throws Exception {
    // Copy property sources to Environment
    MutablePropertySources envPropSources = ((ConfigurableEnvironment) environment).getPropertySources();
    envPropSources.forEach(propertySource -> {
        if (propertySource.containsProperty("application.properties.locations")) {
            locations = ((String) propertySource.getProperty("application.properties.locations")).split(",");
            stream(locations).forEach(filename -> loadProperties(filename).forEach(source ->{
                envPropSources.addFirst(source);
            }));
        }
    });
}


private List<PropertySource> loadProperties(final String filename) {
    YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
    try {
        final Resource[] possiblePropertiesResources = ResourcePatternUtils.getResourcePatternResolver(rl).getResources(filename);
        final Properties properties = new Properties();
        return stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .map(resource1 -> {
                    try {
                        return loader.load(resource1.getFilename(), resource1);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }).flatMap(l -> l.stream())
                .collect(Collectors.toList());
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

}

}