Правильно использовать профили среды Spring для управления PropertySourcesPlaceholderConfigurer и набора файлов свойств
Я работаю над приложением Spring, и я понимаю, что у меня проблема с тем, как я управляю своими свойствами. Я использую Spring профили среды для загрузки моих свойств, и недавно я добавил больше профилей, которые сделали мои файлы свойств неуправляемыми.
Файлы свойств расположены в разных папках в пределах src/main/resources/META-INF/props/
, а папка eah соответствует другому профилю среды Spring.
У меня есть как минимум 5 профилей, что означает, что у меня есть 5 подпапок, каждый из которых содержит файлы свойств с одинаковыми именами, но с разными значениями только для некоторых клавиш.
Вот как это выглядит:
![properties file screen capture]()
Вот как я настроил свой PropertyPlaceholders:
@Configuration
public class PropertyPlaceholderConfiguration {
@Profile(Profiles.CLOUD)
static class cloudConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/cloud/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.DEFAULT)
static class defaultConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/default/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.TEST)
static class testConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/test/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.DEV)
static class devConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/dev/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
...
}
Подводя итог, моя проблема такова:
- пары ключ/значение дублируются по всем 5 различным папкам, так как только несколько значений различаются.
Поэтому я ищу новую стратегию для управления различиями между различными средами.
Кто-нибудь может помочь?
Ответы
Ответ 1
Есть много способов сделать это, но я думаю, что вы на правильном пути.
Перенастройка файлов свойств выполняется через BeanFactoryPostProcessors, и есть две реализации, которые могут помочь вам в этом случае, поэтому вам не нужно делать это с нуля:
PropertySourcesPlaceholderConfigurer и PropertyOverrideConfigurer.
Это пример использования PropertySourcesPlaceholderConfigurer:
<bean id="someProperties" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" abstract="true" >
<property name="locations">
<list>
<value>classpath:database.properties</value>
<value>classpath:email.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>
<bean id="devProperties" parent="someProperties" >
<property name="properties" >
<props >
<prop key="database.username">Database Username used for Development Environment </prop>
</props>
</property>
<property name="localOverride" value="true" />
</bean>
<bean id="testProperties" parent="someProperties" >
<property name="properties" >
<props >
<prop key="database.username">Database Username used for Testing Environment </prop>
</props>
</property>
<property name="localOverride" value="true" />
</bean>
В этом примере вы загружаете свойства по умолчанию в bean, которые будут использоваться в качестве шаблона для других beans, а в конкретном bean, например TestEnvironmentProperties bean или DevEnvironmentProperties bean, вы переопределяете только специфические свойства, которые вы хотите переопределить из файлов свойств по умолчанию. В этом примере показано, как переопределить определенные свойства без необходимости создания другого файла свойств, оттуда вы можете решить, как выбрать, какой bean создать с помощью factory, простой класс фасада или систему профилей, все, что вы как и соответствует вашей архитектуре.
Также, если вы считаете, что этот параметр слишком подробный, вы можете использовать элемент свойство-placeholder.
Я рекомендую вам эту книгу:
Начало работы с Spring Framework, Second Edition
у него есть только примеры, которые вам нужны в 5-й главе. Я не писал это или что-то еще, я только что купил его некоторое время назад, и мне понравилось это после прохождения стольких плохих книг Spring.
Ответ 2
Потяните общие свойства в отдельный файл и укажите это, а также свойства профиля в качестве входных данных для каждого профиля. Не использовали конфигурацию Spring на основе Java, но здесь, как я это делаю в XML. Предположим, что вы можете сделать то же самое в коде:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<beans profile="default">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/local.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="local">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/local.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="trial">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/trial.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="live">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/live.profile.properties</value>
</list>
</property>
</bean>
</beans>
</beans>
Ответ 3
Я думаю, что наткнулся на начало решения с этим интересным сообщением в блоге.
Цитата из статьи:
Остерегайтесь избыточности свойств среды. Например, если решение должно иметь один файл свойств для каждой среды (например, "db-test.properties", "db-dev.properties" и т.д.), затем сохранение этих свойств может быть немного кошмаром - если добавляется свойство "foo", затем оно должно быть добавлено к файл свойств для каждой среды (например, DEV, TEST, PROD и т.д.). PropertyOverrideConfigurer подходит для устранения этого избыточность, задание значения по умолчанию в контексте приложения а затем переопределяющее значение в отдельном файле. это важно, однако, документировать это хорошо, так как это может выглядеть немного "волшебный" для ничего не подозревающего разработчика обслуживания, который видит одно значение указанный в файле контекста, а другой используется во время выполнения.
Идея состоит в том, чтобы полагаться на PropertyOverrideConfigurer и учитывать общие свойства.
Ответ 4
Лучшей практикой является размещение всех файлов свойств за пределами упаковки WAR. Вы можете использовать переменную JNDI, чтобы указать Spring на физический путь, где могут быть прочитаны файлы внешних свойств. Пример:
<jee:jndi-lookup id="externalFileArea" jndi-name="java:comp/env/mgo/externalFileArea"
default-value="/opt/external/props" lookup-on-startup="true"/>
<util:properties id="myConf" location="file:#{externalFileArea}/my-conf.properties"/>
<!-- And now, to use an entry from this properties import -->
<bean id="foo" class="foo.bar.com">
<property name="configParam1" value="#{myConf['fooConfig.param1']}"
</bean>
Если в Windows запись JNDI может быть указана как /C/Users/someone.
Наконец, добавьте файл с именем /opt/external/props/my -conf.properties, и там помещается запись вроде: fooConfig.param1 = true
Вымойте, промойте, повторите. Гораздо меньше работы, гораздо более безопасно и гораздо проще в обслуживании.
Ответ 5
Я бы предположил, что "общие" свойства не обязательно должны быть в общем файле, а вместо этого могут быть значениями по умолчанию, указанными в вашем коде. Это позволяет их переопределить с помощью аргументов JVM (или локальных env), не требуя "управления" в файле. Специфичные для среды свойства в ваших файлах, относящихся к среде, затем указывают только те свойства, которые ДОЛЖНЫ быть предоставлены в каждой среде для запуска приложения. Таким образом, они не будут иметь значения по умолчанию в заполнителях.