Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpEL @ConditionalOnProperty string property empty or nulll

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
like image 738
user3712237 Avatar asked Sep 08 '17 14:09

user3712237


People also ask

What is @ConditionalOnExpression?

Annotation Type ConditionalOnExpressionReferencing a bean in the expression will cause that bean to be initialized very early in context refresh processing. As a result, the bean won't be eligible for post-processing (such as configuration properties binding) and its state may be incomplete.

What is @ConditionalOnProperty in spring?

The Spring framework provides the @ConditionalOnProperty annotation precisely for this purpose. In short, the @ConditionalOnProperty enables bean registration only if an environment property is present and has a specific value. By default, the specified property must be defined and not equal to false.

What is SpEL expression?

The Spring Expression Language (SpEL for short) is a powerful expression language that supports querying and manipulating an object graph at runtime. The language syntax is similar to Unified EL but offers additional features, most notably method invocation and basic string templating functionality.

What is matchIfMissing?

matchIfMissing. public abstract boolean matchIfMissing. Specify if the condition should match if the property is not set. Defaults to false . Returns: if the condition should match if the property is missing Default: false.


2 Answers

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
}
like image 93
sfussenegger Avatar answered Sep 18 '22 07:09

sfussenegger


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.

like image 33
andrii Avatar answered Sep 18 '22 07:09

andrii