Spring Boot和多个外部configuration文件

我有多个属性文件,我想从类path加载。 在/src/main/resources下有一个默认设置,它是myapp.jar一部分。 我的springcontext期望文件在类path。 即

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

我也需要用外部设置覆盖这些属性的选项。 我在cwd有一个外部configuration文件夹。 按照Spring引导文档config文件夹应该在classpath上。 但是从doc不清楚它是否只会覆盖来自那里的applicaiton.properties或者config中的所有属性。

当我testing它时,只有application.properties被拾取,剩下的属性仍然从/src/main/resources被拾取。 我已经尝试提供他们作为逗号分隔列表spring.config.location但默认设置仍然没有被覆盖。

我如何使多个外部configuration文件覆盖默认的?

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

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

我改变了我的applicationcontext上下文

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

这就是我加载应用程序时如何分离文件和类path。
EDITS:

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

我真的不想使用上面的解决方法,并popup覆盖类path上的所有外部configuration文件,就像它为application.properties文件。

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

  1. 命令行参数。
  2. Java系统属性(System.getProperties())。
  3. OS环境variables。
  4. 来自java:comp / env的JNDI属性
  5. 一个RandomValuePropertySource只具有随机的属性*。
  6. 包装jar外的应用程序属性(application.properties,包括YAML和configuration文件变体)。
  7. 打包在jar中的应用程序属性(application.properties包括YAML和configuration文件变体)。
  8. @Configuration类的@PropertySource注释。
  9. 默认属性(使用SpringApplication.setDefaultProperties指定)。

当parsing属性(即@Value("${myprop}")parsing是以相反的顺序(从9开始)完成的。

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

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

上面的那个将添加一个目录,这个目录将被用于application.properties文件。

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

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

默认configuration文件和位置是在spring.config.location指定的spring.config.location之前加载的,意味着后者将始终覆盖之前设置的属性。 (另见“Spring Boot参考指南”的这一部分 )。

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

看看PropertyPlaceholderConfigurer,我发现它比标注更清晰。

例如

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

使用Spring引导,spring.config.location不起作用,只需提供逗号分隔的属性文件。

看下面的代码

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

可以将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 

根据使用spring.profiles.active属性设置的configuration文件值,jdbc.host的值将被拾取。 所以当(在Windows上)

 set spring.profiles.active=dev 

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

对于

 set spring.profiles.active=default 

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

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

 $ chmod 766 application.properties $ chown tomcat application.properties 

我有同样的问题。 我希望能够在启动时用外部文件覆盖内部configuration文件,类似于Spring Boot application.properties检测。 在我的情况下,这是一个user.properties文件,我的应用程序用户存储在那里。

我的要求:

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

  1. 类path
  2. 当前目录的A / config子目录。
  3. 当前目录
  4. 从目录或启动时由命令行参数给出的文件位置

我想出了以下解决scheme:

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

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

这里有一个重要的细节:在@Value批注中,对于properties.location使用空string作为默认值,以避免在未设置属性时出现错误。

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

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

我敢肯定,这个解决scheme可以扩展到处理多个文件…

编辑

在这里,我的解决scheme为多个文件:)像以前一样,这可以结合一个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; } } 

这是一个使用弹簧引导的简单方法

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

app.properties上下文,在您select的位置

 test.one = 1234 

你的春季启动应用程序

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

和预定义的application.properties上下文

 spring.profiles.active = one 

你可以根据自己的喜好编写任意多的configuration类,并通过设置spring.profiles.active =configuration文件名称/名称{以逗号分隔}来启用/禁用它们。

正如你所看到的,春季开机很棒,只需要一段时间才能熟悉,值得一提的是你也可以在你的领域使用@Value

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

弹簧启动允许我们编写不同的configuration文件来写入不同的环境,例如,我们可以为生产,qa和本地环境

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

 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 

同样,我们可以根据需要将application-prod.properties和application-qa.properties编写成许多属性文件

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

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