Problem:
When using @Conditional
bean definitions within @Configuration
classes, properties from external sources, e. g. files, are not available in the Environment
when Condition#matches
is evaluated - unless @PropertySource
is used. Using ConfigurationCondition
instead of Condition
doesn't make a difference, no matter which ConfigurationPhase
is used.
Since I also have the requirement to use wildcards for the names of my properties files, using @PropertySource
is not an option, since it requires the locations to be absolute. Spring Boot is completely off the table, so @ConditionalOnProperty
is not an option either. This leaves me with defining a PropertySourcesPlaceholderConfigurer
bean as the only remaining viable option.
Question:
Is there any way to define beans depending on the presence, absence or value of properties in the Environment
using plain Spring mechanisms (not boot, and not some other hacky way) while also specifying property locations with wildcards and if so, what would need to be done?
Example:
In the following JUnit
test, I expect a bean named bean
to be available, iff the property key
is available (i. e. not null
) in the Environment
during the evaluation of the matches
method in the PropertyCondition
. This is not the case, thus the test fails.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ConditionTest.ConditionTestConfiguration.class })
public class ConditionTest {
private static final String PROPERTIES_LOCATION = "test.properties";
private static final String BEAN_NAME = "bean";
@Autowired
private ApplicationContext applicationContext;
@Test
public void testBeanNotNull() {
assertNotNull(applicationContext.getBean(BEAN_NAME));
}
@Configuration
static class ConditionTestConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setLocation(new ClassPathResource(PROPERTIES_LOCATION));
return propertySourcesPlaceholderConfigurer;
}
@Bean
@Conditional(PropertyCondition.class)
public Object bean() {
return BEAN_NAME;
}
}
static class PropertyCondition implements ConfigurationCondition {
private static final String PROPERTY_KEY = "key";
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty(PROPERTY_KEY) != null;
}
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
}
}
Once I add an @PropertySource
annotation to the ConditionTestConfiguration
like this:
@Configuration
@PropertySource("classpath:test.properties")
static class ConditionTestConfiguration
the property key
is available, thus the PropertyCondition#matches
method evaluates to true, thus bean
is available in the ApplicationContext
and the test passes.
Additional Information:
- A file called
test.properties
containingkey=value
exists in the classpath - I used a
ConfigurationCondition
withConfigurationPhase.REGISTER_BEAN
, just to show that it doesn't change the behaviour for the better - Package declaration and import statements are omitted for brevity