以下のようなコードがあったとして、条件分岐で処理を切り分けるコードについて、
現状では、ObjectA、ObjectBともに@Autowiredをつけているので、コードを実行すると2つのオブジェクトが生成されてしまいます。
フィールド変数に複数のオブジェクト変数を用意するのはポリモーフィズムの観点でも気味が悪いので、インターフェースを使おうとしています。
しかし、nullpointエラーが発生するなどして前に進みません。
どのように対処したらよろしいのでしょうか。
@Autowired ObjectA objA; @Autowired ObjectB objB; @Override public void run(String command) { String msg; if (command == "A") { msg = objA.hoo(); return msg; } if (command == "B") { msg = objB.hoo(); return msg; } }
インターフェース版
@Autowired Objectable obj; @Override public void run(String command) { String msg; if (command == "A") { msg = objA.hoo(); return msg; } if (command == "B") { msg = objB.hoo(); return msg; } }
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
すべてのコードを拝見していないため、ある程度推測で回答することになりますがご了承ください。
現状では、ObjectA、ObjectBともに@Autowiredをつけているので、コードを実行すると2つのオブジェクトが生成されてしまいます。
フィールド変数に複数のオブジェクト変数を用意するのはポリモーフィズムの観点でも気味が悪いので、インターフェースを使おうとしています。
しかし、nullpointエラーが発生するなどして前に進みません。
どのように対処したらよろしいのでしょうか。
上記のご説明と記載コードから以下の通り解釈しましたが相違ないでしょうか?
・ObjectA/ObjectBは同一インターフェースを実装している
・実装クラスが@Autowiredアノテーションを付与しているにも関わらずnullになる
解決したいこと
1.単一変数でObjectA、Bどちらも管理したい
2.上記を満たした上でnullを回避したい
解決したいことに対する回答
まず、nullになる件ですが、実装クラスがComponentScanの対象クラスであるかを確認してください。
ポリモーフィズムを意識して単一変数で取り回した処理例を以下に記載します。
ObjectA、Bを@Componentでコンポーネント化後にMap形式で保持するようにしました。
JAVA
1@Component("A") //Bean名を A でコンポーネント化 2public class ObjectA implements Objectable { 3 @Override 4 public String hoo() { 5 return "hooA"; 6 } 7 8} 9 10@Component("B") //Bean名を B でコンポーネント化 11public class ObjectB implements Objectable { 12 @Override 13 public String hoo() { 14 return "hooB"; 15 } 16}
java
1@Autowired // keyがBean名、valueが実装クラスの実インスタンス 2Map<String, Objectable> hoge; 3 4@Override 5public void run(String command) { 6 7 return hoge.get(command).hoo(); 8}
一応動くであろうコードを記載しました。
しかし、本来であればBean名は意味のある名称にするべきであるため
例えば以下のコードのように、セッター経由で変数に追加する際にそのクラス固有のあたいをキーに
設定してあげるのもアリだと思います。
(動作未確認)
java
1public interface Objectable { 2 public String getCommandName(); // command = A, Bのあたい格納用 3 4 public String hoo(); 5 6}
java
1private Map<String, Objectable> hoge = new HashMap<String, Objectable>(); 2 3@Override 4public void run(String command) { 5 6 return hoge.get(command).hoo();; 7} 8 9@Autowired 10public void setObjectableList(List<Objectable> l) { 11 12 for( Objectable o : l ) { 13 this.hoge.put(o.getCommandName(), o); 14 } 15}
投稿2018/10/04 12:43
編集2018/10/04 12:54総合スコア77
0
ベストアンサー
やりたいことは例えばこういうことなのだろうか…という推察からですが、
java
1@Service 2@RequiredArgsConstructor 3public class DynamicBeanService { 4 5 private final ItemRepository itemRepository 6 7 public String getResult(String type) { 8 return itemRepository.select(); 9 } 10}
としたときに、このItemRepositoryがインタフェースで、これの実装クラスが、
- FileItemRepository
- DatasourceItemRepository
と複数あった場合、@Autowired
やコンストラクタインジェクションしようとしてもSpringBoot起動時のBeanチェックで落ちてしまう(=DIコンテナから見たら、ItemRepositoryの実体を取得しようにも、どちらを取得してよいか判断できない)ので、条件によって取得するBeanを振り分けるようにする例として、SpringBootの設定を使う手法がもっとも簡単でしょうか。
インタフェース
java
1public interface ItemRepository { 2 public String select(); 3}
実装クラス
java
1import org.springframework.stereotype.Repository; 2 3@Repository 4public class FileItemRepository implements ItemRepository { 5 @Override 6 public String select() { 7 return "file"; 8 } 9}
java
1import org.springframework.stereotype.Repository; 2 3@Repository 4public class DatasourceItemRepository implements ItemRepository { 5 @Override 6 public String select() { 7 return "datasource"; 8 } 9}
このような実装をしたときに、SpringBootのBean定義をします。
java
1import org.springframework.beans.factory.annotation.Autowired; 2import org.springframework.beans.factory.config.ConfigurableBeanFactory; 3import org.springframework.context.annotation.Bean; 4import org.springframework.context.annotation.Configuration; 5import org.springframework.context.annotation.Scope; 6 7import com.github.apz.springsample.repository.DatasourceItemRepository; 8import com.github.apz.springsample.repository.FileItemRepository; 9import com.github.apz.springsample.repository.ItemRepository; 10 11@Configuration 12public class DynamicBeanSampleConfig { 13 14 @Autowired 15 FileItemRepository fileItemRepository; 16 17 @Autowired 18 DatasourceItemRepository datasourceItemRepository; 19 20 @Bean 21 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 22 public ItemRepository itemRepository(String type) { 23 switch (type) { 24 case "FILE": 25 return fileItemRepository; 26 case "DATA_SOURCE": 27 return datasourceItemRepository; 28 default: 29 throw new UnsupportedOperationException(); 30 31 } 32 } 33}
この例では、itemRepositoryの名前でDIコンテナに登録します。引数にStringを要求し、その値でどのRepositoryの実態を返すかを判断します。
なお、Scopeは、itemRepositoryのインスタンスが要求されるたびに動的に変わるのでProtoTypeを指定します。
こうすると、このRepositoryを取得する側の実装は、例えば次のようになるでしょう。
java
1import org.springframework.context.ApplicationContext; 2import org.springframework.stereotype.Service; 3 4import com.github.apz.springsample.repository.ItemRepository; 5 6import lombok.RequiredArgsConstructor; 7 8@Service 9@RequiredArgsConstructor 10public class DynamicBeanService { 11 12 private final ApplicationContext context; 13 14 public String getResult(String type) { 15 ItemRepository itemRepository = 16 (ItemRepository)context.getBean("itemRepository", type); 17 18 return itemRepository.select(); 19 } 20}
投稿2018/10/04 14:19
総合スコア12011
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/10/11 08:27
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2018/10/11 07:44
2018/10/11 10:24