spring-bootspring-webfluxkotlin-coroutinesspring-el

Kotlin Coroutines based Spring EL Evaluation failed


Given an interface and an implementation class like this.

interface MyBean{
   suspend fun suspend isEnabled(name:String): Boolean
}

@Service("myBean")
@NoArg // I created an annotation and configured the noarg plugin 
//to satisfy Spring EL's `noarg default constructor` requirements.
class MyMbean(val antoherBean: AnotherBean): MyBean{

   override suspend fun isEnabled(name:String): Boolean{
   }
}

When applied it on the @ConditonalOnExpression like this.

@Configuration
@ConditionalOnExpression("#myBean.isEnabled('test')")
class MyConfig{}

For the EL string, I have tried #{myBean.isEnabled('test')} etc. None worked.

EL1008E: Property or field 'myBean' cannot be found on object of type 
'org.springframework.beans.factory.config.BeanExpressionContext'
 - maybe not public or not valid?

I was using the latest Spring Boot 3.3.1 and Kotlin 2.0.0/Kotlin Coroutines 1.8.x.

Update: I have tried to remove the suspend modifier but still encountered failure.


Solution

  • We normally use @Configuration annotation as place where "core" beans has been defined.

    And then use @Component (or @Service, @Repository) annotation to define "specific" beans which use "core" beans.

    That why MyConfig class will be init before MyMbean class.

    If you want to try do that. I suggest 2 solution:

    Solution 1

    Make MyConfig depends on myBean and move @ConditionalOnExpression on creating bean method:

    @Configuration
    @ConditionalOnBean(name = "myBean")
    public class MyConfig {
    
        @Bean
        @ConditionalOnExpression("@myBean.isEnabled('test')")
        public String simpleBean() {
            return "simpleBean";
        }
    }
    

    Note: use @ to get bean references

    Solution 2

    You have to customize own condition and create bean manually:

    @Configuration
    @Conditional(MyBeanCondition.class)
    public class MyConfig {}
    
    public class MyBeanCondition implements ConfigurationCondition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            MyMbean mybean = context.getBeanFactory().createBean(MyMbean.class);
            return mybean.isEnabled("test");
        }
    
        @Override
        public ConfigurationPhase getConfigurationPhase() {
            return ConfigurationPhase.REGISTER_BEAN;
        }
    }