Last active
January 21, 2024 00:20
-
-
Save tokrug/9b52bc936ae459c31619b6c89ac5acac to your computer and use it in GitHub Desktop.
Spring Boot autoconfiguration conditional annotation on typesafe ConfigurationProperies class. Maps properties to the provided class and executes it's isPresent() method to determine if Conditional should match or not. application.yml, ExampleSpringConfiguration.java and ExampleSpringPropertiesModel.java show how the rest of the code can be used.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
your: | |
properties: | |
prefix: | |
example-configuration-property: 'foobar' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Metannotation for OnConfigurationPropertiesCondition custom conditional implementation. | |
*/ | |
@Target({ElementType.TYPE, ElementType.METHOD}) | |
@Retention(RetentionPolicy.RUNTIME) | |
@Documented | |
@Conditional(OnConfigurationPropertiesCondition.class) | |
public @interface ConditionalOnConfigurationProperties { | |
// properties namespace | |
String prefix(); | |
// configuration properties model class | |
Class<? extends OptionalProperties> targetClass(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Configuration | |
public class ExampleSpringConfiguration { | |
@Bean | |
@ConditionalOnConfigurationProperties(prefix = ExampleSpringPropertiesModel.PREFIX, targetClass = ExampleSpringPropertiesModel.class) | |
@ConditionalOnMissingBean | |
YourType provideDefaultElementOfTheSystem(ExampleSpringPropertiesModel properties) { | |
// this bean factory method will be invoked only if there is no bean of 'YourType' defined elsewhere | |
// and properties in application.yml are provided | |
// then you can construct a bean based on the properties | |
// great for autoconfiguration (spring-boot-starter like modules) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@ConfigurationProperties(ExampleSpringPropertiesModel.PREFIX) | |
@ConstructorBinding | |
@RequiredArgsConstructor | |
@Getter | |
public class ExampleSpringPropertiesModel implements OptionalProperties { | |
public static final String PREFIX = "your.properties.prefix"; | |
private final String exampleConfigurationProperty; | |
@Override | |
public boolean isPresent() { | |
return exampleConfigurationProperty != null; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Custom conditional that checks if properties required by the targetClass | |
* are present in the {@link Environment}. | |
* | |
* targetClass must implement the {@link OptionalProperties} interface in order to define custom logic | |
* checking if properties are in fact set. | |
* | |
* Condition will not match if any of the following statements is true: | |
* <ul> | |
* <li>{@link org.springframework.boot.context.properties.bind.Binder Binder} could not bind properties to the target type</li> | |
* <li>Target class instance was successfully bound but {@link OptionalProperties#isPresent()} returned false</li> | |
* </ul> | |
* | |
*/ | |
public class OnConfigurationPropertiesCondition extends SpringBootCondition { | |
@Override | |
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { | |
MergedAnnotation<ConditionalOnConfigurationProperties> mergedAnnotation = metadata.getAnnotations().get(ConditionalOnConfigurationProperties.class); | |
String prefix = mergedAnnotation.getString("prefix"); | |
Class<?> targetClass = mergedAnnotation.getClass("targetClass"); | |
if (!OptionalProperties.class.isAssignableFrom(targetClass)) { | |
return ConditionOutcome.noMatch("Target type does not implement the OptionalProperties interface."); | |
} | |
Object bean = Binder.get(context.getEnvironment()).bind(prefix, targetClass).orElse(null); | |
if (bean == null) { | |
return ConditionOutcome.noMatch("Binding properties to target type resulted in null value."); | |
} | |
OptionalProperties props = (OptionalProperties) bean; | |
if (props.isPresent()) { | |
return ConditionOutcome.match(); | |
} else { | |
return ConditionOutcome.noMatch("Properties are not present."); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Allows defining custom logic to check if configuration model class is in fact filled with data. | |
*/ | |
public interface OptionalProperties { | |
boolean isPresent(); | |
} |
@Mazzotta13 Added ExampleSpringConfiguration.java, ExampleSpringPropertiesModel.java and application.yml to show how it can all be used together.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Could you provide an example to how to use it?