Background
I came across a piece of code that looks something like this -
public class TestClass {
@Profile("profile")
@Bean(name = "beanMethodName") //I know this is not an accurate way of adding qualifier
public Function<String, String> beanMethodName(final String arg) {
// Do something
}
@Profile("!profile")
@Bean(name = "beanMethodName")
public Function<String, String> beanMethodName(final String arg1, final String arg2) {
// Do something
}
}
Now, when I start this Spring application for profile = test, then the Bean beanMethodName never gets created.
Root Cause
The reason why this happens is how Spring decides which beans to create and which ones to skip. The following code block exists in ConfigurationClassBeanDefinitionReader -
// Do we need to mark the bean as skipped by its condition?
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
Ref -
The first check for this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)
results in TRUE since the Condition for Profile
doesn't match. The Bean Method Name
is added to the skippedBeanMethods
. The second Bean Method Name
check for this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)
results in FALSE, but later in the second if...
block the bean registration is skipped again since the Bean Method Name
is present in skippedBeanMethods
The Problem / Question
The current code block forces the developers to use a separate method name for the bean even if it can probably be an override for a different profile.
I've read in some places that the order of beans defined in the Configuration can help, but that still means that it would fail in the other profile where the non-matching-profile bean comes first.
Question
Why can't the code be modified to be something like this?
// Do we need to mark the bean as skipped by its condition?
boolean shouldSkip = this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
if (configClass.skippedBeanMethods.contains(methodName) && shouldSkip) {
return;
} else if (shouldSkip) {
configClass.skippedBeanMethods.add(methodName);
return;
}
This decouples the Bean Method Name
from the skippedBeanMethods check.
- If a
Bean Method Name
is present in theskippedBeanMethods
AND for the Condition evaluator results inshouldSkip(..)
returning TRUE, then the bean is skipped. - If a
Bean Method Name
is not present in theskippedBeanMethods
AND for the Condition evaluator results inshouldSkip(..)
returning TRUE, then the bean is skipped and it is added to theskippedBeanMethods
. - In all other cases, it would still allow bean registration.
In other words, the bean registration will be skipped if and only if a Condition Evaluator results in shouldSkip(..)
returning TRUE for the Bean Method Name
.
Note
- I understand that the issue here can be resolved by using a different
Bean Method Name
or by using appropriateQualifier
. - What I am trying to understand is why can't the bean registration be decoupled from the
Bean Method Names
?