简体   繁体   English

Spring Boot 和多个外部配置文件

[英]Spring Boot and multiple external configuration files

I have multiple property files that I want to load from classpath.我有多个要从类路径加载的属性文件。 There is one default set under /src/main/resources which is part of myapp.jar . /src/main/resources下有一个默认设置,它是myapp.jar的一部分。 My springcontext expects files to be on the classpath.我的springcontext期望文件在类路径上。 ie 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 .我在cwd中有一个外部配置文件夹。 As per spring boot doc config folder should be on classpath.根据 spring boot doc config 文件夹应该在类路径上。 But its not clear from doc if it will only override the application.properties from there or all the properties in config.但是从文档中不清楚它是否只会从那里覆盖application.properties或配置中的所有属性。

When I tested it, only application.properties gets picked up and rest of properties are still picked up from /src/main/resources .当我测试它时,只有application.properties被拾取,其余属性仍然从/src/main/resources中拾取。 I have tried supplying them as comma separated list to spring.config.location but the default set is still not being overriden.我尝试将它们作为逗号分隔列表提供给spring.config.location但默认设置仍未被覆盖。

How do I make multiple external config files override default ones?如何让多个外部配置文件覆盖默认配置文件?

As workaround I currently used app.config.location (app specific property) which I supply through the command line.作为解决方法,我目前使用了我通过命令行提供的app.config.location (应用程序特定属性)。 ie IE

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

and I changed my applicationcontext to我将我的applicationcontext上下文更改为

<util:properties id="Job1Props" location="{app.config.location}/job1.properties"></util:properties> <util:properties id="Job1Props" location="{app.config.location}/job1.properties"></util:properties>

<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:编辑:

//pseudo 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.properties file.我真的不想使用上述解决方法,并让 Spring 覆盖类路径上的所有外部配置文件,就像它对application.properties文件所做的那样。

UPDATE : As the behaviour of spring.config.location now overrides the default instead of adding to it.更新:由于 spring.config.location 现在的行为覆盖了默认值而不是添加到它。 You need to use spring.config.additional-location to keep the defaults.您需要使用spring.config.additional-location来保持默认值。 This is a change in behaviour from 1.x to 2.x这是从 1.x 到 2.x 的行为变化


When using Spring Boot the properties are loaded in the following order (see Externalized Configuration in the Spring Boot reference guide).使用 Spring Boot 时,属性按以下顺序加载(请参阅 Spring Boot 参考指南中的外部化配置)。

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

When resolving properties (ie @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.location properties 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.properties files.上面的一个将添加一个目录,该目录将用于获取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.location ones meaning that the latter will always override properties set in the earlier ones.默认配置文件和位置在附加指定的spring.config.location之前加载,这意味着后者将始终覆盖在早期设置中的属性。 (See also this section of the Spring Boot Reference Guide). (另请参阅 Spring Boot 参考指南的这一部分)。

If spring.config.location contains directories (as opposed to files) they should end in / (and will be appended with the names generated from spring.config.name before being loaded).如果spring.config.location包含目录(而不是文件),它们应该以 / 结尾(并且在加载之前将附加从spring.config.name生成的名称)。 The default search path classpath:,classpath:/config,file:,file:config/ is always used, irrespective of the value of spring.config.location .始终使用默认搜索路径classpath:,classpath:/config,file:,file:config/ ,与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.这样,您可以在application.properties (或您使用spring.config.name选择的任何其他基本名称)中为您的应用程序设置默认值,并在运行时使用不同的文件覆盖它,保持默认值。

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.可以将默认版本的 jdbc.properties 放入应用程序中。 The external versions can be set lie this.外部版本可以设置在此。

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.根据使用 spring.profiles.active 属性设置的配置文件值,将获取 jdbc.host 的值。 So when (on windows)所以当(在 Windows 上)

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 中获取值。

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. M. Deinum 的非常好的答案是指 Spring Boot 1 的特性。
I will update for Spring Boot 2 here.我将在这里更新 Spring Boot 2。

Environment properties sources and order环境属性来源和顺序

Spring Boot 2 uses a very particular PropertySource order that is designed to allow sensible overriding of values. Spring Boot 2 使用了一个非常特殊的PropertySource顺序,该顺序旨在允许明智地覆盖值。 Properties are considered in the following order:属性按以下顺序考虑:

  • Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).主目录上的 Devtools 全局设置属性(当 devtools 处于活动状态时为 ~/.spring-boot-devtools.properties)。

  • @TestPropertySource annotations on your tests.测试中的@TestPropertySource注释。

  • @SpringBootTest#properties annotation attribute on your tests.测试中的@SpringBootTest#properties注释属性。 Command line arguments.命令行参数。

  • Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).来自SPRING_APPLICATION_JSON的属性(嵌入在环境变量或系统属性中的内联 JSON)。

  • ServletConfig init parameters. ServletConfig初始化参数。

  • ServletContext init parameters. ServletContext初始化参数。

  • JNDI attributes from java:comp/env .来自java:comp/env的 JNDI 属性。

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

  • OS environment variables.操作系统环境变量。

  • A RandomValuePropertySource that has properties only in random.*.仅具有随机属性的RandomValuePropertySource 。*。

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

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

  • Application properties outside of your packaged jar ( application.properties and YAML variants).打包 jar 之外的应用程序属性( application.properties和 YAML 变体)。

  • Application properties packaged inside your jar ( application.properties and YAML variants).打包在 jar 中的应用程序属性( application.properties和 YAML 变体)。

  • @PropertySource annotations on your @Configuration classes. @Configuration类上的@PropertySource注释。 Default properties (specified by setting SpringApplication.setDefaultProperties ).默认属性(通过设置SpringApplication.setDefaultProperties指定)。

To specify external properties files these options should interest you :要指定外部属性文件,您应该对这些选项感兴趣:

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

  • Application properties outside of your packaged jar ( application.properties and YAML variants).打包 jar 之外的应用程序属性( application.properties和 YAML 变体)。

  • @PropertySource annotations on your @Configuration classes. @Configuration类上的@PropertySource注释。 Default properties (specified by setting SpringApplication.setDefaultProperties ).默认属性(通过设置SpringApplication.setDefaultProperties指定)。

You can use only one of these 3 options or combine them according to your requirements.您可以只使用这 3 个选项中的一个,也可以根据您的要求将它们组合起来。
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 .例如,对于非常简单的情况,仅使用特定于配置文件的属性就足够了,但在其他情况下,您可能希望同时使用特定于配置文件的属性、默认属性和@PropertySource

Default locations for application.properties files application.properties 文件的默认位置

About application.properties files (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当前目录的 /config 子目录

  • The current directory当前目录

  • A classpath /config package一个类路径 /config 包

  • The classpath root类路径根

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.默认位置并不总是足够的:默认文件名( application.properties )等默认位置可能不适合。 Besides, as in the OP question you may need to specify multiple configuration files other than application.properties (and variant).此外,与 OP 问题一样,您可能需要指定除application.properties (和变体)之外的多个配置文件。
So spring.config.name will not be enough.所以spring.config.name是不够的。

In this case you should provide an explicit location by using the spring.config.location environment property (which is a comma-separated list of directory locations or file paths).在这种情况下,您应该使用spring.config.location环境属性(以逗号分隔的目录位置或文件路径列表)来提供显式位置。
To be free about the filenames pattern favor the list of file paths over the list of directories.为了自由使用文件名模式,优先考虑文件路径列表而不是目录列表。
For example do like that :例如这样做:

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.location argument adds specified locations in the Spring environment.在 Spring Boot 1 中, spring.config.location参数在 Spring 环境中添加了指定的位置。
But from Spring Boot 2, spring.config.location replaces the default locations used by Spring by the specified locations in the Spring environment as stated in the documentation .但是从 Spring Boot 2 开始, spring.config.location将 Spring 使用的默认位置替换为 Spring 环境中的指定位置,如文档中所述

When custom config locations are configured by using spring.config.location , they replace the default locations.当使用spring.config.location配置自定义配置位置时,它们会替换默认位置。 For example, if spring.config.location is configured with the value classpath:/custom-config/ , file:./custom-config/ , the search order becomes the following:例如,如果spring.config.location配置了值classpath:/custom-config/ , file:./custom-config/ ,则搜索顺序如下:

  1. file:./custom-config/

  2. classpath:custom-config/

spring.config.location is now a way to make sure that any application.properties file has to be explicitly specified. spring.config.location现在是一种确保必须明确指定任何application.properties文件的方法。
For uber JARs that are not supposed to package application.properties files, that is rather nice.对于不应该打包application.properties文件的 uber JAR,这是相当不错的。

To keep the old behavior of spring.config.location while using Spring Boot 2 you can use the new spring.config.additional-location property instead of spring.config.location that still adds the locations as stated by the documentation :要在使用 Spring Boot 2 时保持spring.config.location的旧行为,您可以使用新的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 个要指定的外部属性文件和 1 个包含在 uber jar 中的属性文件。

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.properties is 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在最后一个示例中不是必需的,因为默认位置具有该属性,并且此处的默认位置不会被覆盖而是被扩展。

Take a look at the PropertyPlaceholderConfigurer, I find it clearer to use than annotation.看一下PropertyPlaceholderConfigurer,我觉得用起来比注解更清楚。

eg例如

@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;
    }

this is one simple approach using 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.properties context, 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.properties context和预定义的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 = profile name/names {以逗号分隔}来启用/禁用它们

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;

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.我希望能够在启动时使用外部文件覆盖内部配置文件,类似于 Spring Boot application.properties 检测。 In my case it's a user.properties file where my applications users are stored.在我的情况下,它是一个 user.properties 文件,我的应用程序用户存储在其中。

My requirements:我的要求:

Load the file from the following locations (in this order)从以下位置加载文件(按此顺序)

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

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;
    }

}

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 eg然后编写一些脚本来启动不同环境的应用程序,例如

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

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.我刚刚遇到了类似的问题,终于找到了原因:application.properties 文件的所有权和 rwx 属性错误。 So when tomcat started up the application.properties file was in the right location, but owned by another user:因此,当 tomcat 启动时,application.properties 文件位于正确的位置,但归另一个用户所有:

$ chmod 766 application.properties

$ chown tomcat application.properties

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.在我的 application-dev.yml 中,我添加了这个配置,允许我注入其中包含 -dev.yml 的所有 yml。 This can be a list of specific files also.这也可以是特定文件的列表。 "classpath:/test/test.yml,classpath:/test2/test.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);
    }
}

} }

If you want to override values specified in your application.properties file, you can change your active profile while you run your application and create an application properties file for the profile.如果要覆盖 application.properties 文件中指定的值,可以在运行应用程序时更改活动配置文件并为配置文件创建应用程序属性文件。 So, for example, let's specify the active profile "override" and then, assuming you have created your new application properties file called "application-override.properties" under /tmp, then you can run因此,例如,让我们指定活动配置文件“覆盖”,然后假设您已经在 /tmp 下创建了名为“application-override.properties”的新应用程序属性文件,那么您可以运行

java -jar yourApp.jar --spring.profiles.active="override" --spring.config.location="file:/tmp/,classpath:/" 

The values especified under spring.config.location are evaluated in reverse order. spring.config.location 下指定的值以相反的顺序进行评估。 So, in my example, the classpat is evaluated first, then the file value.因此,在我的示例中,首先评估 classpat,然后评估文件值。

If the jar file and the "application-override.properties" file are in the current directory you can actually simply use如果 jar 文件和“application-override.properties”文件在当前目录中,您实际上可以简单地使用

java -jar yourApp.jar --spring.profiles.active="override"

since Spring Boot will find the properties file for you因为 Spring Boot 会为你找到属性文件

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"在这里,我们覆盖使用“application.yml”来使用“application-MyTest_LowerImportance.yml”和“application-MyTest_MostImportant.yml”
(Spring will also look for .properties files) (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.调试/跟踪非常有用,因为 Spring 会转储它加载的所有文件的名称以及它尝试加载的文件的名称。
You will see lines like this in the console at runtime:您将在运行时在控制台中看到这样的行:

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

You can use like that -Dspring.profiles.active=intranet if you have a application-intranet.properties in your resource folder.如果您的资源文件夹中有 application-intranet.properties,您可以像这样使用-Dspring.profiles.active=intranet Note that: intranet is my specific filename, yours should be different注意:内网是我的特定文件名,你的应该不同

I ran into a lot of issues when trying to figure this out.在试图解决这个问题时,我遇到了很多问题。 Here is my setup,这是我的设置,

Dev Env : Windows 10, Java : 1.8.0_25, Spring Boot : 2.0.3.RELEASE, Spring : 5.0.7.RELEASE开发环境:Windows 10,Java:1.8.0_25,Spring Boot:2.0.3.RELEASE,Spring:5.0.7.RELEASE

What I found is spring is sticking with the concept "Sensible defaults for configuration".我发现 spring 坚持“合理的配置默认值”的概念。 What this translates in to is, you have to have all your property files as part of your war file.这意味着,您必须将所有属性文件作为战争文件的一部分。 Once in there, you can then override them using the "--spring.config.additional-location" command line property to point to external property files.在那里,您可以使用“--spring.config.additional-location”命令行属性覆盖它们以指向外部属性文件。 But this will NOT WORK if the property files are not part of the original war file.但是如果属性文件不是原始战争文件的一部分,这将不起作用。

Demo code: https://github.com/gselvara/spring-boot-property-demo/tree/master演示代码: https ://github.com/gselvara/spring-boot-property-demo/tree/master

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM