1. 序章

このチュートリアルでは、@Conditionalアノテーションを見ていきます。 定義された条件に基づいて、特定のコンポーネントが登録に適格かどうかを示すために使用されます。

事前定義された条件付きアノテーションを使用する方法、それらをさまざまな条件と組み合わせる方法、およびカスタムの条件ベースのアノテーションを作成する方法を学習します。

2. 条件の宣言

実装に移る前に、まず、どのような状況で条件付きアノテーションを利用できるかを見てみましょう。

最も一般的な使用法は、構成クラス全体を含めるか除外することです。

@Configuration
@Conditional(IsDevEnvCondition.class)
class DevEnvLoggingConfiguration {
    // ...
}

または、単一のBean:

@Configuration
class DevEnvLoggingConfiguration {
    
    @Bean
    @Conditional(IsDevEnvCondition.class)
    LoggingService loggingService() {
        return new LoggingService();
    }
}

そうすることで、アプリケーションの動作を特定の条件に基づいて行うことができます。 たとえば、環境のタイプやクライアントの特定のニーズ。 上記の例では、開発環境に対してのみ追加のロギングサービスを初期化します。

コンポーネントを条件付きにする別の方法は、条件をコンポーネントクラスに直接配置することです。

@Service
@Conditional(IsDevEnvCondition.class)
class LoggingService {
    // ...
}

上記の例は、 @Component @Service @Repository、、または@Controllerアノテーションで宣言された任意のBeanに適用できます。

3. 事前定義された条件付き注釈

Springには、事前定義された条件付きアノテーションのセットが付属しています。 最も人気のあるもののいくつかを見てみましょう。

まず、構成プロパティ値に基づいてコンポーネントをベースにする方法を見てみましょう:

@Service
@ConditionalOnProperty(
  value="logging.enabled", 
  havingValue = "true", 
  matchIfMissing = true)
class LoggingService {
    // ...
}

最初の属性value、は、表示する構成プロパティを示します。 2番目のhavingValue、は、この条件に必要な値を定義します。 最後に、 matchIfMissing 属性は、パラメーターが欠落している場合に条件を一致させる必要があるかどうかをSpringに通知します。

同様に、に基づいて条件を設定できます。

@Service
@ConditionalOnExpression(
  "${logging.enabled:true} and '${logging.level}'.equals('DEBUG')"
)
class LoggingService {
    // ...
}

これで、Springは logging.enabled構成プロパティがtrue、logging.levelの両方に設定されている場合にのみLoggingServiceを作成します。 DEBUGに設定されています。

適用できるもう1つの条件は、特定のBeanが作成されたかどうかを確認することです。

@Service
@ConditionalOnBean(CustomLoggingConfiguration.class)
class LoggingService {
    // ...
}

または、特定のクラスがクラスパスに存在します。

@Service
@ConditionalOnClass(CustomLogger.class)
class LoggingService {
    // ...
}

@ConditionalOnMissingBeanまたは@ConditionalOnMissingClassアノテーションを適用することで、逆の動作を実現できます。

さらに、コンポーネントを特定のJavaバージョン依存させることができます。

@Service
@ConditionalOnJava(JavaVersion.EIGHT)
class LoggingService {
    // ...
}

上記の例では、 LoggingService は、ランタイム環境がJava8の場合にのみ作成されます。

最後に、 @ConditionalOnWarDeployment アノテーションを使用して、戦争パッケージでのみBeanを有効にすることができます。

@Configuration
@ConditionalOnWarDeployment
class AdditionalWebConfiguration {
    // ...
}

サーバーが組み込まれているアプリケーションの場合、この条件はfalseを返すことに注意してください。

4. カスタム条件の定義

Springでは、カスタム条件テンプレートを作成することにより、@Conditionalアノテーションの動作をカスタマイズできます。 作成するには、Conditionインターフェイスを実装する必要があります。

class Java8Condition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return JavaVersion.getJavaVersion().equals(JavaVersion.EIGHT);
    }
}

matches メソッドは、条件が合格したかどうかをSpringに通知します。 これには、Beanが初期化されるコンテキストと、使用される@Conditionalアノテーションのメタデータに関する情報をそれぞれ提供する2つの引数があります。

ご覧のとおり、この例では、Javaのバージョンが8であるかどうかを確認するだけです。

その後、新しい条件を@Conditionalアノテーションの属性として配置する必要があります。

@Service
@Conditional(Java8Condition.class)
public class Java8DependedService {
    // ...
}

このように、 Java8DependentService は、Java8Conditionクラスの条件が一致する場合にのみ作成されます。

5. 組み合わせ条件

より複雑なソリューションの場合、ORまたはAND論理演算子を使用して条件付き注釈をグループ化できます。

OR演算子を適用するには、AnyNestedConditionクラスを拡張するカスタム条件を作成する必要があります。 その中に、条件ごとに空の static クラスを作成し、適切な@Conditional実装で注釈を付ける必要があります。

たとえば、Java8またはJava9のいずれかを必要とする条件を作成しましょう。

class Java8OrJava9 extends AnyNestedCondition {
    
    Java8OrJava9() {
        super(ConfigurationPhase.REGISTER_BEAN);
    }
    
    @Conditional(Java8Condition.class)
    static class Java8 { }
    
    @Conditional(Java9Condition.class)
    static class Java9 { }
    
}

一方、AND演算子ははるかに単純です。 条件を簡単にグループ化できます。

@Service
@Conditional({IsWindowsCondition.class, Java8Condition.class})
@ConditionalOnJava(JavaVersion.EIGHT)
public class LoggingService {
    // ...
}

上記の例では、 LoggingService は、IsWindowsConditionJava8Conditionの両方が一致する場合にのみ作成されます。

6. 概要

この記事では、条件付きアノテーションの使用方法と作成方法を学びました。 いつものように、すべてのソースコードはGitHub利用できます。