Может ли @PropertySources выбираться профилем Spring?
У меня есть Spring 3.1 @Configuration
, которому требуется свойство foo
для построения bean. Свойство определено в defaults.properties
, но может быть переопределено свойством в overrides.properties
, если приложение имеет активный профиль override
Spring.
Без переопределения код будет выглядеть так и работать...
@Configuration
@PropertySource("classpath:defaults.properties")
public class MyConfiguration {
@Autowired
private Environment environment;
@Bean
public Bean bean() {
...
// this.environment.getRequiredProperty("foo");
...
}
}
Я хотел бы @PropertySource
для classpath:overrides.properties
контингента на @Profile("overrides")
. Есть ли у кого-нибудь идеи о том, как это можно достичь? Некоторые параметры, которые я рассмотрел, являются дубликатами @Configuration
, но это будет нарушать DRY или программные манипуляции с ConfigurableEnvironment
, но я не уверен, куда будет идти вызов environment.getPropertySources.addFirst()
.
Размещение следующего в конфигурации XML работает, если я вставляю свойство непосредственно с помощью @Value
, но не тогда, когда я использую метод Environment
и getRequiredProperty()
.
<context:property-placeholder ignore-unresolvable="true" location="classpath:defaults.properties"/>
<beans profile="overrides">
<context:property-placeholder ignore-unresolvable="true" order="0"
location="classpath:overrides.properties"/>
</beans>
Обновление
Если вы пытаетесь сделать это сейчас, ознакомьтесь с Spring Boot поддержкой YAML, в частности "Использование YAML вместо свойств", раздел. Поддержка профиля там сделает этот вопрос спорным, но пока не поддерживается @PropertySource
.
Ответы
Ответ 1
Добавить переопределение @PropertySource
в статическом внутреннем классе. К сожалению, вы должны указать все источники источников вместе, что означает создание профиля "по умолчанию" в качестве альтернативы "переопределить".
@Configuration
public class MyConfiguration
{
@Configuration
@Profile("default")
@PropertySource("classpath:defaults.properties")
static class Defaults
{ }
@Configuration
@Profile("override")
@PropertySource({"classpath:defaults.properties", "classpath:overrides.properties"})
static class Overrides
{
// nothing needed here if you are only overriding property values
}
@Autowired
private Environment environment;
@Bean
public Bean bean() {
...
// this.environment.getRequiredProperty("foo");
...
}
}
Ответ 2
Вы можете сделать:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
Изменить: если вам нужно что-то более продвинутое, вы можете зарегистрировать свои PropertySources при запуске приложения.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
создаваемый файл:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String[] profiles = applicationContext.getEnvironment().getActiveProfiles()
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Как только вы это сделали, вам просто нужно добавить в свой контекст:
<context:property-placeholder/>
Я не могу ответить на ваш вопрос о нескольких профилях, но я думаю, вы активируете их в таком инициализаторе, и вы можете зарегистрировать соответствующие элементы PropertySource во время активации профиля.
Ответ 3
Я не могу думать иначе, чем тот, который вы предложили Emerson, который должен определить этот bean в отдельном файле @Configuration
с аннотацией @Profile
:
@Configuration
@Profile("override")
@PropertySource("classpath:override.properties")
public class OverriddenConfig {
@Autowired
private Environment environment;
@Bean
public Bean bean() {
//if..
}
}
Ответ 4
Примечание.. Этот ответ предоставляет альтернативное решение для использования файлов свойств с @PropertySource
. Я пошел по этому маршруту, потому что он слишком громоздкий, пытаясь работать с несколькими файлами свойств, которые могут иметь переопределения, избегая повторяющегося кода.
Создайте POJO-интерфейс для каждого связанного набора свойств, чтобы определить их имена и типы.
public interface DataSourceProperties
{
String driverClassName();
String url();
String user();
String password();
}
Реализовать, чтобы вернуть значения по умолчанию.
public class DefaultDataSourceProperties implements DataSourceProperties
{
public String driverClassName() { return "com.mysql.jdbc.Driver"; }
...
}
Подкласс для каждого профиля (например, разработка, производство) и переопределение любых значений, которые отличаются от значений по умолчанию. Для этого требуется набор взаимоисключающих профилей, но вы можете легко добавить "default" в качестве альтернативы "переопределениям".
@Profile("production")
@Configuration
public class ProductionDataSourceProperties extends DefaultDataSourceProperties
{
// nothing to override as defaults are for production
}
@Profile("development")
@Configuration
public class DevelopmentDataSourceProperties extends DefaultDataSourceProperties
{
public String user() { return "dev"; }
public String password() { return "dev"; }
}
Наконец, автоувертируйте конфигурации свойств в другие конфигурации, которые в них нуждаются. Преимущество здесь в том, что вы не повторяете код создания @Bean
.
@Configuration
public class DataSourceConfig
{
@Autowired
private DataSourceProperties properties;
@Bean
public DataSource dataSource() {
BoneCPDataSource source = new BoneCPDataSource();
source.setJdbcUrl(properties.url());
...
return source;
}
}
Я все еще не убежден, что я буду придерживаться этого, чтобы вручную настроить файлы свойств на основе активных профилей в инициализаторе контекста сервлета. Моя мысль заключалась в том, что выполнение ручной настройки не было бы таким же поддающимся модульной проверке, но теперь я не уверен. Я действительно предпочитаю читать свойства свойств в списке аксессуаров свойств.
Ответ 5
Если вам нужно поддерживать несколько профилей, вы можете сделать что-то вроде этого:
@Configuration
public class Config {
@Configuration
@Profile("default")
@PropertySource("classpath:application.properties")
static class DefaultProperties {
}
@Configuration
@Profile("!default")
@PropertySource({"classpath:application.properties", "classpath:application-${spring.profiles.active}.properties"})
static class NonDefaultProperties {
}
}
Таким образом, вам не нужно определять статический класс конфигурации для каждого профиля.
Спасибо, Дэвид Харкнесс, за то, что направил меня в правильном направлении.