1

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 the skippedBeanMethods AND for the Condition evaluator results in shouldSkip(..) returning TRUE, then the bean is skipped.
  • If a Bean Method Name is not present in the skippedBeanMethods AND for the Condition evaluator results in shouldSkip(..) returning TRUE, then the bean is skipped and it is added to the skippedBeanMethods.
  • 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 appropriate Qualifier.
  • What I am trying to understand is why can't the bean registration be decoupled from the Bean Method Names?
Suyash Khandwe
  • 386
  • 3
  • 11

0 Answers0