web-dev-qa-db-ja.com

スプリングダイナミックインジェクション、工場のようなパターン

依存性注入、遅延注入の実践 からの続き。私はメインクラスを持っています:

package test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import Java.util.List;
import Java.util.Scanner;

@Component
public class Main {
    @Autowired
    private StringValidator stringValidator;

    @Autowired
    private StringService stringService;

    @Autowired
    private ValidationService validationService;

    public void main() {
        scanKeyboardCreateLists();

        stringValidator.validate();

        final List<String> validatedList = stringValidator.getValidatedList();
        for (String currentValid : validatedList) {
            System.out.println(currentValid);
        }
    }

    private void scanKeyboardCreateLists() {
        //Let's presume the user interacts with the GUI, dynamically changing the object graph...
        //Needless to say, this is past container initialization...
        Scanner scanner = new Scanner(System.in);
        int choice = scanner.nextInt();

        //Delayed creation, dynamic
        if (choice == 0) {
            stringService.createList();
            validationService.createList();
        } else {
            stringService.createSecondList();
            validationService.createSecondList();
        }
    }

    public static void main(String[] args) {
        ApplicationContext container = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml");
        container.getBean(Main.class).main();
    }
}

また、オブジェクトグラフは、ユーザーの操作に応じて動的に作成されます。アプリケーションの結合を解決し、これを非常に簡単にテストできるようにしました。 また、リストはコンテナによって維持されるため、このアプリケーション(および他のすべて)の動的な性質は関係ありません。アプリケーションがリストを必要とするときにいつでも要求でき、要素を維持できるからです。

残りのコードはここにあります:

package test;

import Java.util.List;

public interface Stringable {
    List<String> getStringList();
}

package test;

import org.springframework.stereotype.Component;

import Java.util.ArrayList;

@Component
public class StringList extends ArrayList<String> {
}

package test;

import org.springframework.stereotype.Component;

import javax.inject.Inject;
import Java.util.ArrayList;
import Java.util.List;

@Component
public class StringService implements Stringable {

    private List<String> stringList;

    @Inject
    public StringService(final ArrayList<String> stringList) {
        this.stringList = stringList;
    }

    //Simplified
    public void createList() {
        stringList.add("FILE1.txt");
        stringList.add("FILE1.dat");
        stringList.add("FILE1.pdf");
        stringList.add("FILE1.rdf");
    }

    public void createSecondList() {
        stringList.add("FILE2.txt");
        stringList.add("FILE2.dat");
        stringList.add("FILE3.pdf");
        stringList.add("FILE3.rdf");
    }

    @Override
    public List<String> getStringList() {
        return stringList;
    }
}

package test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import Java.util.ArrayList;
import Java.util.List;

@Component
public class StringValidator {
    private List<String> stringList;
    private List<String> validationList;

    private final List<String> validatedList = new ArrayList<String>();

    @Autowired
    public StringValidator(final ArrayList<String> stringList,
                           final ArrayList<String> validationList) {
        this.stringList = stringList;
        this.validationList = validationList;
    }

    public void validate() {
        for (String currentString : stringList) {
            for (String currentValidation : validationList) {
                if (currentString.equalsIgnoreCase(currentValidation)) {
                    validatedList.add(currentString);
                }
            }
        }
    }

    public List<String> getValidatedList() {
        return validatedList;
    }
}

package test;

import Java.util.List;

public interface Validateable {
    List<String> getValidationList();
}

package test;

import org.springframework.stereotype.Component;

import Java.util.ArrayList;

@Component
public class ValidationList extends ArrayList<String> {
}

package test;

import org.springframework.stereotype.Component;

import javax.inject.Inject;
import Java.util.ArrayList;
import Java.util.List;

@Component
public class ValidationService implements Validateable {

    private List<String> validationList;

    @Inject
    public ValidationService(final ArrayList<String> validationList) {
        this.validationList = validationList;
    }

    //Simplified...
    public void createList() {
        validationList.add("FILE1.txt");
        validationList.add("FILE2.txt");
        validationList.add("FILE3.txt");
        validationList.add("FILE4.txt");
    }

    public void createSecondList() {
        validationList.add("FILE5.txt");
        validationList.add("FILE6.txt");
        validationList.add("FILE7.txt");
        validationList.add("FILE8.txt");
    }

    @Override
    public List<String> getValidationList() {
        return validationList;
    }
}

デザインをかなり強制するコンストラクターを使用せずに、createList()またはcreateSecondList()というメソッド呼び出しを解決する方法を知っている人はいますか?私は工場を考えていましたが、より大きな規模のプロジェクトのすべてのクラスの工場は良い考えではないようです。

何かのようなもの:

<bean ... factory-method="..." depends-on="..." lazy-init="..."/>

そして、ファクトリメソッドでクラスをインスタンス化し、メソッドcreateList()を呼び出します。または、あるメソッドからこのように呼び出します。これも見栄えが悪く、メソッドにオブジェクトグラフをインスタンス化する責任を負わせます。

ランタイムで解決したいランタイム依存関係の図は次のとおりです。

enter image description here

ユーザーの操作に応じて、コンテナーを使用して動的な遅延初期化を実現する方法は他にありますか?

ありがとうございました。

15
pfh

クラスの一部のメンバーを、対応するゲッターを呼び出すたびに動的に初期化/入力する必要がある場合は、ルックアップメソッドインジェクションを試すことができます。 ppを読んでください。 3.3.4.1ここ

したがって、動的メンバーを含むクラスがscope=singletone(Spring Beanコンテナーのデフォルト)で作成された場合でも、ルックアップメソッドが割り当てられているフィールドにアクセスするたびに、ビジネスロジックに従って適切なオブジェクトを取得します。 lookupメソッド内に実装されています。あなたの場合、リストはインターフェースなので、ルックアップメソッド内に検証を簡単に実装して、検証済みのリストを返すことができます。

編集:

Springのドキュメントで の方が良いと思いました-非常に明確だと思います。 「3.4.6.1ルックアップメソッドインジェクション」をご覧ください

Mainクラスを構成するとき、ルックアップメソッドをそのListメンバーに割り当てます-ListBeanの新しいインスタンスが必要になるたびに呼び出されます。

幸運を!

10
aviad

Springは、ビジネスデータの操作や挿入ではなく、再利用可能なコンポーネントの挿入用に設計されています。

実際、一部のデータは依存性注入で使用されますが、コンポーネントの動作を構成するためだけに使用され、ビジネスデータホルダーを作成するためには使用されません。

ちなみに、あなたのケースでは次のオプションを使用できます:BeanFactory with BeanFactoryAware interface とscope = "prototype"の使用により、getBean()を呼び出すことでBeanを生成できます。 その例 または その他の質問:オンデマンドでBeanを作成する のように。

準備するBeanの数が限られている場合の代替オプションは、一般的なBean作成を使用することです 不足しているBeanがモックされるのと同じ方法

ここで、SpringがそのコンテキストでBeanをガベージ収集しないことを考慮してください。したがって、ビジネスデータを保持するSpring Beanを作成すると、メモリを消費するリスクがあります。

目的が異なる場合(そう願っています)、マルチテナントサポートを独自に実装しようとしている可能性があります。 Springは、特定のコンポーネントまたは動作で実装するビジネスコンテキストが異なる場合に備えて、tenancyを提供します。

3
Yves Martin

ユーザーがオブジェクトの1..Nグラフを選択でき、実行時にユーザーが選択したグラフのみをロードしたいようです。グラフが設計時にわかっているが、ユーザーが必要なグラフを選択するだけの場合、あなたが持っているのは一連のApplicationContextであり、ユーザーが実行時に選択した1つのApplicationContextのみをロードしたいように思えます。では、ApplicationContextのセットを定義してから、実行時に適切なものをインスタンス化してみませんか。 SpringはJava Configをサポートしているため、これらの構成をJavaクラスとして定義すると、継承を取得してコードの切り取り/貼り付けを回避できるようになります。

2
dough