I have two factory methods:
@Bean
@ConditionalOnProperty("some.property.text")
public Apple createAppleX() {}
and
@Bean
@ConditionalOnProperty("some.property.text", matchIfMissing=true)
public Apple createAppleY() {}
In case of not having "some.property.text" property at all - second method works fine and first one is ignored, which is desired behavior.
In case we have some string set to "some.property.text" - both methods are considered valid for producing Apple objects, which leads the application to fail with error "No qualifying bean of type".
Is it possible to avoid second method to be considered as factory method in case we have some value for the property? Especially, is it possible via annotations only?
It allows to load beans conditionally depending on a certain environment property: @Configuration @ConditionalOnProperty( value="module. enabled", havingValue = "true", matchIfMissing = true) class CrossCuttingConcernModule { ... } The CrossCuttingConcernModule is only loaded if the module.
The @ConditionalOnProperty annotation allows you to load beans conditionally depending on a certain environment property or configuration of a property. Use the prefix and name attributes to specify the property that should be checked. By default, any property that exists and is not equal to false is matched.
Annotation Type ConditionalOnProperty. @Conditional that checks if the specified properties have a specific value. By default the properties must be present in the Environment and not equal to false . The havingValue() and matchIfMissing() attributes allow further customizations.
Conditions based on a Bean definition are present in Spring Application context. Conditions based on a Bean object are present in Spring Application context. Conditions based on some or all Bean properties values. Conditions based on some Resources are present in current Spring Application Context or not.
I was facing same problem and here is my solution:
@Bean
@ConditionalOnProperty("some.property.text")
public Apple createAppleX() {}
@Bean
@ConditionalOnProperty("some.property.text", matchIfMissing=true, havingValue="value_that_never_appears")
public Apple createAppleY() {}
You can use NoneNestedConditions
to negate one or more nested conditions. Something like this:
class NoSomePropertyCondition extends NoneNestedConditions {
NoSomePropertyCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty("some.property.text")
static class SomePropertyCondition {
}
}
You can then use this custom condition on one of your bean methods:
@Bean
@ConditionalOnProperty("some.property.text")
public Apple createAppleX() {}
@Bean
@Conditional(NoSomePropertyCondition.class)
public Apple createAppleY() {}
In the spirit of one-upmanship, Here is a more reusable annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Conditional(ConditionalOnMissingProperty.MissingPropertyCondition.class)
public @interface ConditionalOnMissingProperty {
String PROPERTY_KEYS = "propertyKeys";
@AliasFor(PROPERTY_KEYS)
String[] value() default {};
@AliasFor("value")
String[] propertyKeys() default {};
class MissingPropertyCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
String[] keys = (String[]) metadata.getAnnotationAttributes(ConditionalOnMissingProperty.class.getName()).get(PROPERTY_KEYS);
if (keys.length > 0) {
boolean allMissing = true;
for (String key : keys) {
String propertyValue = context.getEnvironment().getProperty(key);
String propertyValueList = context.getEnvironment().getProperty(key + "[0]"); //in case of list
allMissing &= (StringUtils.isEmpty(propertyValue) && StringUtils.isEmpty(propertyValueList));
}
if (allMissing) {
return new ConditionOutcome(true, "The following properties were all null or empty in the environment: " + Arrays.toString(keys));
}
return new ConditionOutcome(false, "one or more properties were found.");
} else {
throw new RuntimeException("expected method annotated with " + ConditionalOnMissingProperty.class.getName() + " to include a non-empty " + PROPERTY_KEYS + " attribute");
}
}
}
}
Annotated beans or configuration are activated when one or more mentioned properties all do not exist:
@ConditionalOnMissingProperty({"app.foo.bar"})
@Configuration
public class SomeConfiguration {
//... won't run if there is an app.foo.bar property with non-empty contents.
}
Later if I add more comprehensive reporting to the false outcome, I'll add it here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With