Spring ConditionalOnProperty for external properties
Asked Answered
H

2

10

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.

Hourihan answered 10/2, 2018 at 3:50 Comment(0)
B
11

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.

Brigid answered 10/2, 2018 at 10:40 Comment(0)
E
0

Although this question is quite old, however, I would like to add my findings here for this issue in case it helps someone facing this problem.

we were on spring boot 2.0.1.RELEASE and at least for that version (may be for higher versions also) the property value from external properties file i.e. @PropertySource, for @ConditionalOnProperty works if we have the @PropertySource("your/properties/file/path") annotation is on the main class or on a configuration class in the BOOT-INF/classes which is either imported or componentscanned by the main class. If the same javaconfig class resides in a jar within BOOT-INF/lib, then it doesn't work.

P.S. we recently migrated from spring boot 2.0.1.RELEASE to spring boot 2.7.12 and it still works.

Escort answered 21/11, 2023 at 5:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.