web-dev-qa-db-ja.com

春のプロファイルのコレクションを無効にできますか(!)

プロファイルのグループで使用されないようにBeanを構成することはできますか?現在私はこれを行うことができます(私は信じています):

@Profile("!dev, !qa, !local")

これを達成するためのきちんとした表記はありますか?プロファイルがたくさんあるとしましょう。また、いくつかのサービス(または何でも)のモックおよび具体的な実装がある場合、それらの1つに注釈を付けるだけで、他のすべてのケースでもう1つが使用されると想定できますか?言い換えれば、これはたとえば必要ですか?

@Profile("dev, prof1, prof2")
public class MockImp implements MyInterface {...}

@Profile("!dev, !prof1, !prof2") //assume for argument sake that there are many other profiles
public class RealImp implements MyInterface {...}

そのうちの1つに注釈を付けて、@Primaryもう一方にアノテーションを付けますか?

本質的に私はこれが欲しい:

@Profile("!(dev, prof1, prof2)")

前もって感謝します!

13
HellishHeat

短い答えは:できません。
しかし、@Conditionalアノテーションのおかげで存在するきちんとした回避策があります。

条件マッチャーを作成します。

public abstract class ProfileCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        if (matchProfiles(conditionContext.getEnvironment())) {
            return ConditionOutcome.match("A local profile has been found.");
        }
        return ConditionOutcome.noMatch("No local profiles found.");
    }

    protected abstract boolean matchProfiles(final Environment environment);
}

public class DevProfileCondition extends ProfileCondition {
   private boolean matchProfiles(final Environment environment) {    
        return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
            return prof.equals("dev") || prof.equals("prof1")) || prof.equals("prof2"));
        });
    }
}

public class ProdProfileCondition extends ProfileCondition {
   private boolean matchProfiles(final Environment environment) {    
        return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
            return !prof.equals("dev") && !prof.equals("prof1")) && !prof.equals("prof2"));
        });
    }
}

これを使って

@Conditional(value = {DevProfileCondition.class})
public class MockImpl implements MyInterface {...}

@Conditional(value = {ProdProfileCondition.class})
public class RealImp implements MyInterface {...}

ただし、このアプローチにはSpringbootが必要です。

13
Anthony Raymond

私が理解していることから、あなたがしたいことは、特定のプロファイルについて、いくつかのBeanをいくつかのスタブ/モックBeanに置き換えることができることです。これに対処するには2つの方法があります。

  • 対応するプロファイルに不要なBeanを除外し、デフォルトでその他すべてを含める
  • 各プロファイルに必要なBeanのみを含める

最初のオプションは実現可能ですが困難です。これは、@Profileアノテーションで複数のプロファイルを提供する場合のSpringのデフォルトの動作がOR条件であるためです(ケースで必要なANDではありません)。この理想的なSpringの動作はより直観的です。理想的には、各プロファイルはアプリケーションの各構成(実稼働、単体テスト、統合テストなど)に対応している必要があるため、常に1つのプロファイルのみがアクティブになる必要があります。これがORがプロファイル間でANDよりも理にかなっている理由です。この結果、おそらくプロファイルをネストすることにより、この制限を回避できますが、構成が非常に複雑で保守性が低い。

したがって、2番目のアプローチを使用することをお勧めします。アプリケーションの構成ごとに1つのプロファイルを用意します。すべての構成で同じであるすべてのBeanは、@Profileが指定されていないクラスに常駐できます。その結果、これらのBeanはすべてのプロファイルによってインスタンス化されます。異なる構成ごとに区別する必要がある残りのBeanについては、@Configurationが対応するプロファイルに設定されたすべての@Profileクラスを(Springプロファイルごとに)個別に作成する必要があります。このようにして、どんな場合でも注入されるものを簡単に処理できます。

これは次のようになります。

@Profile("dev")
public class MockImp implements MyInterface {...}

@Profile("prof1")
public class MockImp implements MyInterface {...}

@Profile("prof2")
public class MockImp implements MyInterface {...}

@Profile("the-last-profile") //you should define an additional profile, not rely on excluding as described before
public class RealImp implements MyInterface {...}

最後に、@Primaryアノテーションを使用して、既存のBeanをオーバーライドします。同じタイプのBeanが2つある場合、@Primaryアノテーションがないと、Springからインスタンス化エラーが発生します。いずれかのBeanに@Primaryアノテーションを定義した場合、エラーは発生せず、このBeanはこのタイプが必要なすべての場所に挿入されます(他のBeanは無視されます)。ご覧のとおり、これは単一のプロファイルがある場合にのみ役立ちます。そうでなければ、これも最初の選択肢として複雑になります。

TL; DR:はい、できます。タイプごとに、プロファイルごとに1つのBeanを定義し、このプロファイルのみで@Profileアノテーションを追加します。

1
Dimos