Feature Flags
Feature flags (also feature toggles) are useful when you want the ability to turn some application behavior on or off. Let’s say you are rolling out a new functionality. It might be the case that you don’t want to make it available from the get go, maybe part of the functionality is not complete. It might also be the case that you only want to show this functionality to a subset of users. That old-fashioned way of doing this is using conditional statements
if (bazFunctionalityEnabled) {
showBaz();
} else {
showFoo();
}
There are several libraries for aiding in the process of creating feature toggles. Also Spring ships with an annotation called @ConditionalOnProperty
that
is also helpful on those scenarios. This annotation tells Spring whether or not to create a Bean
based on a property. Let’s say you have a Bean
for listening
to a queue
@Component
public class FooSubscriber {
private final MessageConverter<Event> messageConverter;
private FooService fooService;
@Autowired
public FooSubscriber(FooService fooService) {
this.messageConverter = new MessageConverter<>(Event.class);
this.fooService = fooService;
}
@RabbitListener(queues = {"#{@fooQueue}"})
public void receiveMessage(byte[] message) {
Event event = messageConverter.convert(message);
fooService.save(e.getId());
}
}
You might want to disable processing of messages. Maybe the server is too busy processing messages. Just add conditional annotation
@Component
@ConditionalOnProperty(name = "feature.enable.foo.processing", matchIfMissing = true)
public class FooSubscriber {
(...)
}
This way, if feature.enable.foo.processing
is equal to true
in application.properties
the bean will be created and consequently messages will be
processed. Otherwise, Spring won’t create the bean. Notice the argument matchIfMissing
with a value of true
. It tells Spring to consider the property
as being true if it is not defined. This way you can define a default behavior in case that property is not defined.
This is even more convenient if you use distributed configuration tools like Spring Cloud Config. If you want to turn a certain feature off just change the configuration in Cloud Config repository, push those changes, restart the app and you will have the desired result.
In order to check more than one property, just supply an array of property names in the name
parameter
@ConditionalOnProperty(name = {"feature.enable.message.processing", "feature.enable.foo.processing"}, matchIfMissing = true)
See more in the official docs.
Advanced Checks with @ConditionalOnExpression and Spring Expression Language
If you need more elaborated checks you can use Spring Expression Language (SpEL) expressions inside @ConditionalOnExpression
annotations. Example:
@Component
@ConditionalOnExpression("${messaging.enable} && ${messaging.throttle} < 10")
public class FooSubscriber {
(...)
}
Bean Creation on Class Properties
You can also conditionally define multiple beans inside a class and use the presence or absence of this class as flag for another class. It is necessary to
extend AllNestedConditions
abstract class from Spring
public class FooMessagingFlag extends AllNestedConditions {
@Bean
@ConditionalOnProperty("feature.enable.messaging")
publiMessagingBean
}
@Conditional({FooMessagingFlag.class})
@Configuration