Spring Java Config: how do you create a prototype-scoped @Bean with runtime arguments? Ask Question

Spring Java Config: how do you create a prototype-scoped @Bean with runtime arguments? Ask Question

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;
    }
}

注意name:finalこれはコンストラクター経由でのみ提供でき、不変性を保証します。その他の依存関係はクラスの実装固有の依存関係でありThing、リクエスト ハンドラーの実装に認識されるべきではありません (密接に結合されている必要があります)。

このコードは、Spring XML 構成で完全に機能します。次に例を示します。

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

Java 構成で同じことを実現するにはどうすればよいですか? 以下は Spring 3.x では機能しません。

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

今私はできたファクトリーを作成します。例:

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

でもあのServiceLocatorとFactoryデザインパターンをSpringで置き換える意味が全くなくなる、このユースケースには最適です。

Spring Java Config がこれを実行できれば、次の問題を回避できます。

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

これは、Spring が XML 構成を介してすでにサポートしている非常に些細なことに対して、膨大な作業です (比較的に言えば)。

ベストアンサー1

クラスでは@Configuration@Bean次のようなメソッド

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

登録に使用されますBeanの定義とBeanを作成するためのファクトリーを提供する定義される Bean は、直接またはスキャンによって決定される引数を使用して、要求時にのみインスタンス化されますApplicationContext

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

からBeanを取得できますApplicationContextBeanFactory#getBean(String name, Object... args)方法は、

明示的なコンストラクター引数/ファクトリ メソッド引数を指定して、Bean 定義で指定されたデフォルト引数 (存在する場合) をオーバーライドできます。

パラメーター:

引数静的ファクトリ メソッドへの明示的な引数を使用してプロトタイプを作成する場合に使用する引数。それ以外の場合は、null 以外の args 値を使用することは無効です。

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

あるいは、入力したBeanFactory#getBean(Class requiredType, Object... args)タイプ別に Bean を検索するメソッド。

これは少なくとも Spring バージョン 4 以降に当てはまります。

Beanの取得にApplicationContextまたはから開始したくない場合は、BeanFactoryObjectProvider(Spring 4.3 以降)。

インジェクション ポイント専用に設計されたバリアントでObjectFactory、プログラムによるオプション性と、一意でない処理の寛容性を実現します。

そしてそれを使用するgetObject(Object... args)方法

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

次のような明示的な構築引数を指定できますBeanFactory.getBean(String, Object)

例えば、

@Autowired
private ObjectProvider<Thing> things;

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

おすすめ記事