プロパティの有無/値によってBean登録するクラスを切り替える(Spring Boot)

@ConditionalOnProperty

SpringBootが提供しているアノテーションで、ある特定のプロパティが指定の値かどうかチェックを行う。
チェック結果がtrueの場合、アノテーションを付与しているメソッドが実行され、bean登録される。

アノテーションの属性には以下が指定可能

属性 デフォルト値 説明
value String {} プロパティのキー名を指定する。属性nameと同じ。
name String {} プロパティのキー名を指定する。
havingValue String "" プロパティの値を指定する。
matchIfMissing boolean false プロパティが存在していなかったときに動作させるかどうか
prefix String "" 各プロパティに共通のプレフィックス

以下はプロパティの値と属性havingValueの値の組み合わせと、チェック結果を表に表したものである。

プロパティの値 havingValueの値 チェック結果
true ""
true "true"
true "false" ×
true "2020" ×
false "" ×
false "true" ×
false "false"
false "2020" ×
2020年 ""
2020年 "true" ×
2020年 "false" ×
2020年 "2020年"
値なし ""
値なし "true" ×
値なし "false" ×
値なし "2020年" ×

アノテーションを付与した例を示す。

@Configuration
public class SampleConfig {

    // プロパティ「sample.dummy」の値が「2020」の場合bean登録される
    @Bean("sampleHelper")
    @ConditionalOnProperty(value = "sample.dummy", havingValue = "2020")
    public SampleHelper systemDateHelper() {
        System.out.println("systemDateHelper");
        return new SysDateHelper();
    }

    // プロパティ「sample.dummy」がない場合、bean登録される
    // プロパティ「sample.dummy」がある場合、値が何であってもbean登録される
    @Bean("sampleHelper")
    @ConditionalOnProperty(value = "sample.dummy", matchIfMissing = true)
    public SampleHelper dummyDateHelper() {
        System.out.println("dummyDateHelper");
        return new DummyDateHelper();
    }
}

このアノテーションでは、プロパティに値が設定されているかどうかでDIするクラスを切り替えることができない。
任意の条件でDIするクラスを切り替えるようにしたい場合はアノテーションを自作することができる。


@ConditionalOnExpression

SpringBootが提供しているアノテーションで、SpEL式の結果がtrueの場合、アノテーションを付与しているメソッドが実行され、bean登録される。

アノテーションの属性には以下が指定可能

属性 デフォルト値 説明
value String "true" SpEL式

アノテーションを付与した例を示す。

@Configuration
public class SampleConfig {

    // プロパティ「sample.dummy」の値が空の場合bean登録される
    @Bean("sampleHelper")
    @ConditionalOnExpression("#{'${sample.dummy}' matches '^$'}")
    public SampleHelper systemDateHelper() {
        System.out.println("systemDateHelper");
        return new SysDateHelper();
    }
}

任意の条件でチェックするアノテーションの作成方法

プロパティが存在していて、かつ値が空でないことをチェックするアノテーションを作成する例を示す。
1. アノテーションを定義する。
@Conditionalを付与して、属性に定義したアノテーションの処理クラスを指定する。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnPropertyNotEmptyCondition.class)
public @interface ConditionalOnPropertyNotEmpty {

    /** プロパティのキー名 */
    String value();
}

2. アノテーションの処理クラスを実装する。
org.springframework.context.annotation.Conditionを継承する。
matchesメソッドに具体的なチェック処理を記述する。

public class OnPropertyNotEmptyCondition implements Condition {

    /**
     * {@inheritDoc}
     */
    @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();
    }
}

作成したアノテーションの使用例を以下に示す。

@Configuration
public class SampleConfig {

    // プロパティ「sample.dummy」が存在していて、かつ値が空でない場合にbean登録される 
    @Bean("sampleHelper")
    @ConditionalOnPropertyNotEmpty(value = "sample.dummy")
    public SampleHelper dummyDateHelper() {
        return new DummyDateHelper();
    }

}