Spring Basics Conditional Bean Wiring
In Spring, it’s possible to wire a bean only when some condition is met.
A Use Case for Conditional Bean Wiring
Suppose we want to enable caching for our web application. We are
interested to find out whether this helps our application or whether the
overhead will so big that it actually slows our application down. We
decide we want to put the new functionality behind a feature toggle. We
add a new property to our application called
app.caching.enabled=false
. When we are ready to enable the caching, we
change the property’s value to true
and we are in business. When the
results disappoint, we can easily revert the property’s value to
false
.
@Conditional
Introduction
The @Conditional
annotation can be used on any type or method that
declares a bean:
@Conditional(SomeCondition.class)
@Bean
public SomeBean someBean() {
return new SomeBean();
}
@Conditional
takes a mandatory class that implements the functional interface
Condition
.
Condition
defines a single method matches
that returns a boolean.
The method decides whether the bean should be loaded into the Spring
context or whether it should be ignored:
public SomeCondition implements Condition {
public boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
/* make decision based on context and metadata */
}
}
A @Conditional
@Configuration
When @Conditional
is applied to a class which is also annotated with
@Configuration
, then all of the @Bean
methods, @Import
,
@ComponentScan
and other annotations will be subject to the condition.
This means that when the condition evaluates to false
, all of the
configuration defined in that class will be ignored.
Since creating a cache carries costs with it (even an unused cache reserves space on the heap for its initial size), this is exactly what we want in our situation.
@Configuration
@Conditional(CacheCondition.class)
public class CacheConfig {
/* ... define cache manager and caches here ... */
public static CacheCondition implements ConfigurationCondition {
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getRequiredProperty("app.caching.enabled", Boolean.class);
}
}
}
Since we are dealing with a conditional configuration instead of a
regular bean, the condition class implements
ConfigurationCondition
instead of Condition
. The ConfigurationCondition
makes us implement
another method that specifies at which point the condition should be
evaluated. Normally, this
ConfigurationPhase
is set to REGISTER_BEAN
, which means it checks the condition when
adding a regular bean. In this case, we want it set to
PARSE_CONFIGURATION
, which makes Spring evaluate the condition at the
moment the configuration class is parsed. If the condition does not
match at that moment, the @Configuration
class will not be added to
the context.
Dependencies
The only dependency is on spring-context
:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.1.RELEASE</version>
</dependency>
Sample Project
A sample project can be found on GitLab.