'@ConditionalOnMissingBean and @ConditionalOnBean on the same type
I've got strange situation. I'm trying to write auto configuration class where I create bean if another bean exists or not so I've got conditional on bean and conditional on missing bean on same type..
Here is my code:
@Bean
@ConditionalOnMissingBean(
type = {"com.example.Api"}
)
public ApiManager apiManager() {
return new ApiManager() {
public Result getValue(Request request) {
throw new Exception("Api not provided.");
}
public Result setValue(Request request) {
throw new Exception("Api not provided.");
}
};
}
@Bean
@ConditionalOnBean(
type = {"com.example.Api"}
)
public ApiManager apiManager(Api api) {
return new ApiManagerImpl(api);
}
The problem is that it does not check @ConditionalOnBean if it already checked in @ConditionalOnMissingBean that bean of type com.example.Api is not missing and then bean is not created.
And I get error like:
Parameter 2 of constructor in com.example.ServiceImpl required a bean of type 'com.example.ApiManager' that could not be found.
- Bean method 'apiManager' in 'ApiAutoConfiguration' not loaded because @ConditionalOnMissingBean (types: com.example.Api; SearchStrategy: all) found bean 'api'
- Bean method 'apiManager' in 'ApiAutoConfiguration' not loaded because @ConditionalOnMissingBean (types: com.example.Api; SearchStrategy: all) found bean 'api'
Solution 1:[1]
What you get is quite logical. Conditions are evaluated in order, that's why auto-configurations are processed after user-configuration.
If the context did what you're expecting, we'd have to evaluate all the conditions of all the bean definitions before doing anything. And that will defeat the purpose of an auto-configuration handling the fact a bean of a certain type was provided by a user (user config) or an auto-configuration.
You need to rewrite this auto-configuration to import things in order. One way to do that is to move each method on a package private config class and then do as follows:
@Configuration
@Import({ApiPresentConfiguration.class, ApiNotPresentConfiguration.class})
A better way to handle this really is to not do that at all and simply use ObjectProvider:
@Bean
public ApiManager apiManager(ObjectProvider<Api> api) {
// Check if API is present and then return the appropriate implementation
}
Solution 2:[2]
try leveraging @Primary.
I am not sure how this would fit your use case but consider the following:
@Bean
public ApiManager apiManager() {
return new ApiManager() {
public Result getValue(Request request) {
throw new Exception("Api not provided.");
}
public Result setValue(Request request) {
throw new Exception("Api not provided.");
}
};
}
@Bean
@ConditionalOnBean(
type = {"com.example.Api"}
)
@Primary
public ApiManager apiManager(Api api) {
return new ApiManagerImpl(api);
}
Case 1: Api doesn't exist
- First bean acts as the default one and is used.
- Second bean is not created at all.
Case 2: Api does exist
- First bean is created (since it's the default) but not used.
- Second bean is created and you end up with 2 beans of type
ApiManager; but since it has@Primary; it will be used.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Stephane Nicoll |
| Solution 2 | Mahmoud K. |
