Home » Java » Feature-Toggle for Spring components

Feature-Toggle for Spring components

Posted by: admin December 28, 2021 Leave a comment

Questions:

I have a Spring Boot application with a lot of @Component, @Controller, @RestController annotated components. There are about 20 different features which I would like to toggle separatly. It is important that features can be toggled without rebuilding the project (a restart would be ok). I think Spring configuration would be a nice way.

I could image a config (yml) like this:

myApplication:
  features:
    feature1: true
    feature2: false
    featureX: ...

The main problem is that I don’t want to use if-blocks in all places. I would prefer to disable the components completely. For example a @RestController should even be loaded and it should not register its pathes. I’m currently searching for something like this:

@Component
@EnabledIf("myApplication.features.feature1")  // <- something like this
public class Feature1{
   // ...
}

Is there a feature like this? Is there an easy way to implement it myself? Or is there another best practice for feature toggles?

Btw: Spring Boot Version: 1.3.4

Answers:

You could use @ConditionalOnProperty annotation:

@Component
@ConditionalOnProperty(prefix = "myApplication.features", name = "feature1")
public class Feature1{
   // ...
}

###

Conditionally Enabled bean – null when disabled

@Component
@ConditionalOnProperty(prefix = "myApplication.features", name = "feature1", havingValue="true")
public class Feature1 {
    //...
}

@Autowired(required=false)
private Feature1 feature1;

If the conditional bean is a Controller, than you won’t need to autowire it, since the controller is usually not injected. If the conditional bean is injected, you’ll get a No qualifying bean of type [xxx.Feature1] when it is not enabled, which is why you need to autowire it using required=false. It will then remain null.

Conditional Enabled & Disabled beans

If the Feature1 bean is injected in other components, you may inject it using required=false or define the bean to return when the feature is disabled:

@Component
@ConditionalOnProperty(prefix = "myApplication.features", name = "feature1", havingValue="true")
public class EnabledFeature1 implements Feature1{
    //...
}

@Component
@ConditionalOnProperty(prefix = "myApplication.features", name = "feature1", havingValue="false")
public class DisabledFeature1 implements Feature1{
    //...
}

@Autowired
private Feature1 feature1;

Conditional Enabled & Disabled beans – Spring Config:

@Configuration
public class Feature1Configuration{
    @Bean
    @ConditionalOnProperty(prefix = "myApplication.features", name = "feature1", havingValue="true")
    public Feature1 enabledFeature1(){
        return new EnabledFeature1();
    }

    @Bean
    @ConditionalOnProperty(prefix = "myApplication.features", name = "feature1", havingValue="false")
    public Feature1 disabledFeature1(){
        return new DisabledFeature1();
    }
}

@Autowired
private Feature1 feature1;

Spring profiles

Another option would be to activate the beans through spring profiles: @Profile("feature1"). However, all enabled features would have to be listed in a single property spring.profiles.active=feature1, feature2..., so I believe this is not what you want.

###

Try to look at ConditionalOnExpression

and maybe this should work

@Component
@ConditionalOnExpression("${myApplication.controller.feature1:false}")  // <- something like this
public class Feature1{
   // ...
}

###

FF4J is a framework to implement Feature Toggle pattern. It proposes a spring-boot starter and allows to enable or disable spring components at runtime through dedicated web console. By using AOP it allows to inject dynamically the correct beans based on states of feature. It does not add or remove bean from spring context.