前回の記事で Spring Framework による簡単な DI を説明した。
これはプロパティの型を手掛かりに Spring が DI してくれていて、"byType" によるインジェクションという。
では、プロパティの型と同じクラスが2つ以上存在した場合はどちらをDIしてくれるのだろうか。
今回は bean の名前によるインジェクションである "byName" によるインジェクションを紹介する。
"byType" なのにプロパティの型と同じクラスが2つ以上存在した場合の挙動
フォルダ構成はこう。
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.yyama.bean" /> </beans>
[org.yyama.bean]パッケージ配下のクラスをSpringコンテキストの管理対象としている。
Mainクラス
package org.yyama; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.yyama.bean.Fuga; public class Main { public static void main(String... args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Fuga fuga = ctx.getBean(Fuga.class); fuga.proc(); ctx.close(); } }
Spring コンテナから Fuga のインスタンス fuga を取得し、fuga.proc() メソッドを実行している。
Fuga クラス
package org.yyama.bean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Fuga { @Autowired() HogeInterface hoge; public void proc() { hoge.print(); } }
ここの hoge プロパティには HogeInterface をインプリメントした実装クラスがインジェクションされるのだが、今回はHogeInterface をインプリメントしたクラスが2つ存在する。その場合どのような挙動になるのかを実験する。
HogeInterfaceインターフェース
package org.yyama.bean; public interface HogeInterface { public void print(); }
HogeImplAクラス
package org.yyama.bean; import org.springframework.stereotype.Component; @Component public class HogeImplA implements HogeInterface { @Override public void print() { System.out.println("HogeImplAのprint()"); } }
print() メソッドで "HogeImplAのprint()" と出力している。
HogeImplBクラス
package org.yyama.bean; import org.springframework.stereotype.Component; @Component public class HogeImplB implements HogeInterface { @Override public void print() { System.out.println("HogeImplBのprint()"); } }
print() メソッドで "HogeImplBのprint()" と出力している。
Mainクラスを実行すると・・・
エラーが出力された。簡単に訳すと「fugaの生成に失敗した。hogeフィールドの依存性が満たされない。[org.yyama.bean.HogeInterface]が定義されているが、これにひとつだけ一致することを期待していたが、2つ見つかった。hogeImplAとhogeImplBだ。」のように出力されている。
8 10, 2016 8:55:40 午前 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 08:55:40 JST 2016]; root of context hierarchy 8 10, 2016 8:55:40 午前 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 情報: Loading XML bean definitions from class path resource [applicationContext.xml] 8 10, 2016 8:55:41 午前 org.springframework.context.support.ClassPathXmlApplicationContext refresh 警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fuga': Unsatisfied dependency expressed through field 'hoge': No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fuga': Unsatisfied dependency expressed through field 'hoge': No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83) at org.yyama.Main.main(Main.java:8) Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:172) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1065) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1019) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:566) ... 15 more
byName によるインジェクションに修正する。
Fuga クラス、HogeImplA クラス、HogeImplB クラスを修正する。
Fuga クラス
package org.yyama.bean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class Fuga { @Autowired() @Qualifier("hogeA") HogeInterface hoge; public void proc() { hoge.print(); } }
"@Autowired()" の下に、"@Qualifier("hogeA")" を追加した。これで [hogeA] という名前の bean をインジェクションするようSpring に要求している。
HogeImplAクラス
package org.yyama.bean; import org.springframework.stereotype.Component; @Component("hogeA") public class HogeImplA implements HogeInterface { @Override public void print() { System.out.println("HogeImplAのprint()"); } }
"@Component" アノテーションに引数を追加している。これでこのクラスのインスタンスは "hogeA" という名前で Spring に管理される。
HogeImplB クラス
package org.yyama.bean; import org.springframework.stereotype.Component; @Component("hogeB") public class HogeImplB implements HogeInterface { @Override public void print() { System.out.println("HogeImplBのprint()"); } }
もうお分かりだろうが、"@Component" アノテーションに引数を追加している。これでこのクラスのインスタンスは "hogeB" という名前で Spring に登録される。
これで "HogeInterface" をインプリメントした実装クラスは Spring コンテナ内に2つ存在するが、名前が異なるので見分けがつくということだ。
実行結果
8 10, 2016 9:11:18 午前 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 09:11:18 JST 2016]; root of context hierarchy 8 10, 2016 9:11:18 午前 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 情報: Loading XML bean definitions from class path resource [applicationContext.xml] HogeImplAのprint() 8 10, 2016 9:11:18 午前 org.springframework.context.support.ClassPathXmlApplicationContext doClose 情報: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 09:11:18 JST 2016]; root of context hierarchy
Fuga クラスで "@Qualifier("hogeA")" と指定しているので、Fuga クラスの hoge プロパティには "hogeA" という名前の bean がインジェクションされたことがわかる。
今日はここまで。
Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発
- 作者: 株式会社NTTデータ
- 出版社/メーカー: 翔泳社
- 発売日: 2016/07/21
- メディア: 大型本
- この商品を含むブログ (1件) を見る
[改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ
- 作者: 長谷川裕一,大野渉,土岐孝平
- 出版社/メーカー: 技術評論社
- 発売日: 2016/06/14
- メディア: Kindle版
- この商品を含むブログを見る