SpEL @ConditionalOnProperty string property empty or nulll

16,095

Solution 1

You could use @ConditionalOnExpression with a Spring Expression Language (SpEL) expression as value:

@ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${database.url:}')")
@Bean
public DataSource dataSource() {
    // snip
}

For better readability and reusability you could turn this into an annotation:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${database.url:}')")
public @interface ConditionalOnDatabaseUrl {
}

@ConditionalOnDatabaseUrl
@Bean
public DataSource dataSource() {
    // snip
}

Solution 2

I'm having the same issue. As I undestood, you'r looking for "property exists and not empty" condition. This condition doesn't come out of the box, therefore some coding required. Solution using org.springframework.context.annotation.Conditional works for me:

  1. Create ConditionalOnPropertyNotEmpty annotation and accompanied OnPropertyNotEmptyCondition class as follows:

    // ConditionalOnPropertyNotEmpty.java
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.util.Map;
    
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Conditional(ConditionalOnPropertyNotEmpty.OnPropertyNotEmptyCondition.class)
    public @interface ConditionalOnPropertyNotEmpty {
        String value();
    
        class OnPropertyNotEmptyCondition implements Condition {
    
            @Override
            public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
                Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnPropertyNotEmpty.class.getName());
                String propertyName = (String) attrs.get("value");
                String val = context.getEnvironment().getProperty(propertyName);
                return val != null && !val.trim().isEmpty();
            }
        }
    }
    
  2. Annotate your bean with ConditionalOnPropertyNotEmpty, like:

    @ConditionalOnPropertyNotEmpty("database.url")
    @Bean
    public DataSource dataSource() { ... }
    

--

Alternatively, you can either set detabase.url to false explicitly (e.g. detabase.url: false) or do not define detabase.url at all in your config. Then, @ConditionalOnProperty(value = "database.url") will work for you as expected.

Solution 3

You can try more generic purpose annotation which is standard for the Spring Core: org.springframework.context.annotation.Conditional.

In the context objects for your callback you can get all the information you need: org.springframework.context.annotation.Condition

Also you can consider using Profiles.

Share:
16,095
user3712237
Author by

user3712237

Updated on June 26, 2022

Comments

  • user3712237
    user3712237 almost 2 years

    I am currently having trouble with my dataSource bean creation on condition of String property from my applications.yaml file.

    Ideally, I would only like to create the dataSource bean only if the url is set in my application.yaml file. Shouldn't create the bean if its not present (empty or null). I know this condition checks on boolean but is there anyway to check if the string property is empty or null?

    DatabaseConfig.java

    @Configuration
    public class DatabaseConfig {
        @Value("${database.url:}")
        private String databaseUrl;
    
        @Value("${database.username:}")
        private String databaseUsername;
    
        @Value("${database.password:}")
        private String databasePassword;
    
        protected static final String DRIVER_CLASS_NAME = "com.sybase.jdbc4.jdbc.SybDriver";
    
    
        /**
         * Configures and returns a datasource. Optional
         *
         * @return A datasource.
         */
        @ConditionalOnProperty(value = "database.url")
        @Bean
        public DataSource dataSource() {
            return DataSourceBuilder.create()
                .url(testDatabaseUrl)
                .username(testDatabaseUsername)
                .password(testDatabasePassword)
                .build();
        }
    }
    

    application.yml (This field will be optional)

    database:
      url: http://localhost:9000
    
  • Hong
    Hong over 2 years
    May I know why we need generic type over there? T(org.springframework.util.StringUtils)
  • sfussenegger
    sfussenegger over 2 years
    @Hong the T(..) is to reference types and has nothing to do with generics. I assume you were thinking of <T>. Here's the SpEL reference: docs.spring.io/spring-framework/docs/current/reference/html/‌​…