7

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 containing key=value exists in the classpath
  • I used a ConfigurationCondition with ConfigurationPhase.REGISTER_BEAN, just to show that it doesn't change the behaviour for the better
  • Package declaration and import statements are omitted for brevity
aebblcraebbl
  • 151
  • 7

0 Answers0