Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring ConditionalOnProperty for external properties

It seems ConditionalOnProperty only works for properties within the classpath like application.properties in the resources folder. I need a property that an end-user can turn on and off via an external property. An example is extremely simple:

Configuration class, reads the external properties. Sys.out to show it's reading the file properly.

@Configuration
@EnableAutoConfiguration
@PropertySource("file:/Users/end.user/MyApp/config/MyApp.properties")
public class PropertyConfigurer {
    @Value("${featureOne}")
    private String featureOne;

    @PostConstruct
    public void init() {
        System.out.println("FeatureOne : " + featureOne);
    }
}

Feature class, this component class will be put in the application context to be able to be used if the property is enabled via ConditionalOnProperty, otherwise the component is never instantiated.

@Component
@ConditionalOnProperty(name="featureOne", havingValue = "true")
public class FeatureOne {
    @PostConstruct
    public void init() {
        System.out.println("Feature initialized");
    }
}

As you can imagine I am never seeing "Feature initialized" due to the "featureOne" property not being available to the spring context until after this class has been constructed. If there was some way to force the properties from @PropertySource to be available to the spring context upon class instantiation. Or any other way? I also tried @DependsOn the PropertyConfigurer from FeatureOne but that interestingly didn't work either.

like image 509
Aerith Avatar asked Feb 10 '18 03:02

Aerith


People also ask

How do you use conditional property in spring?

The Spring framework provides the @ConditionalOnProperty annotation precisely for this purpose. In short, the @ConditionalOnProperty enables bean registration only if an environment property is present and has a specific value. By default, the specified property must be defined and not equal to false.

What is matchIfMissing?

matchIfMissing. public abstract boolean matchIfMissing. Specify if the condition should match if the property is not set. Defaults to false . Returns: if the condition should match if the property is missing Default: false.

What is @conditional in spring boot?

The @ConditionalOnMissingBean is one common example that is used to allow developers to 'override' auto-configuration if they are not happy with your defaults. Spring Boot includes a number of @Conditional annotations that you can reuse in your own code by annotating @Configuration classes or individual @Bean methods.

How do you conditionally load a bean?

To conditionally create a bean, we must first create an implementation of Condition. The Condition interface contains the matches method which returns a boolean value. Here, the AuditEnabledCondition class is checking whether audit. enabled is true using the Environment properties.


1 Answers

It seems ConditionalOnProperty only works for properties within the classpath like application.properties in the resources folder.

Not exactly. It also works with external files, provided that they are specified as program arguments during running with spring.config.location option.

--spring.config.location=file:/Users/end.user/MyApp/config/MyApp.properties

The problem is @PropertySource is being read by org.springframework.context.annotation.ConfigurationClassParser::processPropertySource method. And @ConditionalOnProperty is being validated at org.springframework.boot.autoconfigure.condition.OnPropertyCondition::getMatchOutcome method.
If you were to put a debug at these two places, you will find that getMatchOutcome is executed first and then processPropertySource. And hence your condition doesn't work with @PropertySource.

But if you were to run your application as java -jar abc.jar --spring.config.location=file:/Users/end.user/MyApp/config/MyApp.properties, then these properties are added to context.environment and hence @ConditionalOnProperty works.

If there was some way to force the properties from @PropertySource to be available to the spring context upon class instantiation

I am not sure if there is any way to do this. But given your requirement (I need a property that an end-user can turn on and off via an external property), using spring.config.location would be a prudent choice.

like image 149
pvpkiran Avatar answered Sep 27 '22 16:09

pvpkiran