1. ホーム
  2. java

[解決済み] Spring Java Config:実行時引数を持つプロトタイプスコープの@Beanを作成する方法は?

2022-04-25 22:11:53

質問

SpringのJava Configを使用して、実行時にのみ取得可能なコンストラクタ引数を持つプロトタイプ・スコープのBeanを取得/インスタンス化する必要があります。 以下のコード例を考えてみましょう(簡潔にするために簡略化しています)。

@Autowired
private ApplicationContext appCtx;

public void onRequest(Request request) {
    //request is already validated
    String name = request.getParameter("name");
    Thing thing = appCtx.getBean(Thing.class, name);

    //System.out.println(thing.getName()); //prints name
}

ここで、Thingクラスは以下のように定義されています。

public class Thing {

    private final String name;

    @Autowired
    private SomeComponent someComponent;

    @Autowired
    private AnotherComponent anotherComponent;

    public Thing(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

お知らせ namefinal : コンストラクタを介してのみ供給可能であり、不変性を保証します。 その他の依存関係は、実装固有の依存関係である Thing クラスは、リクエストハンドラの実装に知られてはいけません (密結合されてはいけません)。

このコードは、例えばSpring XMLのconfigで完全にうまく動作します。

<bean id="thing", class="com.whatever.Thing" scope="prototype">
    <!-- other post-instantiation properties omitted -->
</bean>

Java の config で同じことを実現するにはどうしたらいいですか? Spring 3.xを使用すると、以下はうまくいきません。

@Bean
@Scope("prototype")
public Thing thing(String name) {
    return new Thing(name);
}

さて、私は できる をファクトリーで作成する、など。

public interface ThingFactory {
    public Thing createThing(String name);
}

しかし、その Springを使用してServiceLocatorとFactoryのデザインパターンを置き換えた意味がなくなります。 このユースケースには理想的なのですが。

Spring Java Configでこれができれば、回避できるのですが。

  • ファクトリーインターフェースの定義
  • ファクトリーの実装を定義する
  • ファクトリー実装のテストを書く

SpringがXMLコンフィグで既にサポートしているような些細なことなのに、(相対的に見れば)これは大変な作業です。

どのように解決するのか?

@Configuration クラスでは @Bean メソッドを使用します。

@Bean
@Scope("prototype")
public Thing thing(String name) {
    return new Thing(name);
}

を登録するために使用されます。 を作成するためのファクトリーを提供します。 . このビーンが定義するBeanは,リクエストに応じて,直接またはスキャンによって決定される引数を使用してインスタンス化されるだけです。 ApplicationContext .

の場合は prototype ビーンでは、毎回新しいオブジェクトが作成されるため、対応する @Bean メソッドも実行されます。

からBeanを取得することができます。 ApplicationContext を使用して、その BeanFactory#getBean(String name, Object... args) というメソッドがあります。

明示的なコンストラクタ引数/ファクトリメソッドの指定を可能にする。 の引数で指定されたデフォルトの引数を上書きします。 ビーン定義

パラメータです。

引数 明示的な引数を使用してプロトタイプを作成する場合に使用する引数です。 を静的ファクトリーメソッドに追加します。NULL でない args 値を使用することは無効です。 を使用します。

つまり、この prototype スコープされたビーンでは、ビーンクラスのコンストラクタで使用される引数を提供するのではなく @Bean メソッドを呼び出すことができます。(このメソッドは、ビーンの名前検索を使用するため、型保証が非常に弱いです)。

あるいは、型付き BeanFactory#getBean(Class requiredType, Object... args) メソッドは、ビーンをタイプ別に検索します。

これは少なくともSpringのバージョン4以上では正しい。

なお、もし最初に ApplicationContext または BeanFactory をビーン取得のために注入することができます。 ObjectProvider (Spring 4.3以降)。

のバリアントです。 ObjectFactory インジェクションポイントのために特別に設計されています。 プログラム的なオプションと寛大なnot-uniqueの処理を可能にする。

を使用し、その getObject(Object... args) メソッド

オブジェクトのインスタンス(共有または独立)を返します。 このファクトリーによって管理されます。

に沿って、明示的な構築引数を指定できるようにする。 の BeanFactory.getBean(String, Object) .

例えば、こんな感じです。

@Autowired
private ObjectProvider<Thing> things;

[...]
Thing newThing = things.getObject(name);
[...]