SpringFrameworkの特徴的な機能のひとつであるDIコンテナについて書いていきます。
SpringFrameworkについての記事はこちら olafnosuke.hatenablog.com
DIコンテナとは
SpringがJavaのインスタンスを管理している入れ物。
管理方法:key-value方式
例: sampleService:newSampleService("a");
Springが管理しているインスタンスのことをコンポーネントやbeanと呼ぶ。 DIコンテナにインスタンスを登録することをbean定義と呼ぶ。
Beanのスコープ
スコープとは、DIコンテナに登録されたコンポーネント(インスタンス)の「生存期間」のこと。
スコープには以下のものがある。
prototype
Beanを取得するたびに、毎回インスタンスが生成される
request
HTTP Request単位でインスタンスを生成する
ex. 何かボタンを押してリクエストが返ってくるまで
session
HTTP Session単位でインスタンスを生成する
ex. ユーザーがログインしてからログアウトするまで
singleton(デフォルト)
Spring起動時にインスタンスを1つだけ生成する。生成後は、1つのインスタンスを共有して使う
ApplicationContext単位のスコープ
application
サーブレットのコンテキスト単位でインスタンスが生成される
application スコープは複数のApplicationContext内で同一のコンポーネントを使用する
Bean定義の方法
XMLファイルで定義する方法とJavaConfigで定義する方法、アノテーションで定義する方法がある。
xmlファイルで定義する
<beans>
要素の子要素の<bean>
要素に個々のBean定義を記述する。
Bean名 | Beanインスタンス |
---|---|
id属性で指定した値 | class属性で指定したクラス ※class属性は完全修飾クラス名を記述する |
以下に記述例を示す
<bean id="sampleService" class="jp.co.sample.SampleServiceImpl" />
JavaConfigで定義する
クラスに@Configurationアノテーションを付与し、Bean定義を行うクラスであることを宣言する。
@Configurationアノテーションを付与したクラスのメソッドに@Beanアノテーションを付与し、Bean定義を記述する。
Bean名 | Beanインスタンス |
---|---|
デフォルトでは@beanアノテーションを付与したメソッド名 指定したい場合は、@Bean(name = “別名”)とする。 |
@beanアノテーションを付与したメソッドの戻り値 |
以下に記述例を示す
@Configuration public class AppConfig { @Bean Sample sampleService(){ return new SampleServiceImpl(); } }
アノテーションで定義する
DIコンテナに管理させたいBean(もしくはコンポーネント)に@Componentアノテーションを付与し、@ComponentScanアノテーションでスキャンすることでDIコンテナに登録する。
Bean名 | Beanインスタンス |
---|---|
デフォルトでは@Componentを付与したクラス名の先頭を小文字にしたもの。 指定したい場合は@Component("別名") |
@Componentを付与したクラス |
以下に記述例を示す
package jp.co.sample.service; // DIコンテナに登録したいクラス @Component public class SampleServiceImpl implements SampleService { }
@ComponentScanの属性「basePackages」でスキャン対象のパッケージを指定することが出来る。
// スキャンを行うクラス @Configuration @ComponentScan(basePackages = "jp.co.sample") public class AppConfig { @Bean SampleService sampleService(){ return new SampleServiceImpl(); } }
インジェクション方法
インジェクションとは、DIコンテナに登録したコンポーネントを他のDIコンテナに登録されているクラスで使用できるように注入することである。
インジェクションには、コンストラクタインジェクション、セッターインジェクション、アノテーションインジェクションがある。
コンストラクタインジェクション
コンストラクタインジェクションは、コンポーネントのコンストラクタで依存するコンポーネントを注入する方法である。
以下にSampleクラスにSampleServiceクラスをインジェクションする例を示す。
インジェクションを行いたいクラスで、インジェクションしたいコンポーネントを引数にもつコンストラクタを定義する。
// インジェクション対象のクラス public class Sample { private SampleService service; public Sample(SampleService service){ this.service = service; } }
JavaConfigでbean定義を行う場合、以下のようにSampleクラスをDIコンテナに登録する際に定義したコンストラクタを使用してインスタンスを作成するようにする。
@Configuration public class AppConfig { @Bean Sample sampleService(){ return new SampleServiceImpl(); } @Bean Sample sample(){ return new Sample(sampleService()); } }
XMLでbean定義を行う場合は以下のように記述する。
<bean id="sampleService" class="jp.co.sample.service.SampleServiceImpl" /> <bean id="sample" class="jp.co.sample.Sample"> <constructor-arg ref="sampleService"/> </bean>
アノテーションベースの定義の場合はコンストラクタに@Autiwiredを付与する。
public class Sample { private SampleService service; @Autowired public Sample(SampleService service){ this.service = service; } }
セッターインジェクション
セッターインジェクションはコンポーネントのセッターの引数に依存するコンポーネントを注入する方法である。
public class Sample { private SampleService service; public void setSampleService(SampleService service){ this.service = service; } }
JavaConfigでbean定義を行う場合、以下のようにSampleクラスをDIコンテナに登録する際に定義したセッターを使用してインジェクションを行う。
@Configuration public class AppConfig { @Bean Sample sampleService(){ return new SampleServiceImpl(); } @Bean Sample sample(){ Sample sample = new Sample(); sample.setSampleService(sampleService()); return sample; } }
XMLでbean定義を行う場合は以下のように記述する。
<bean id="sampleService" class="jp.co.sample.service.SampleServiceImpl" /> <bean id="sample" class="jp.co.sample.Sample"> <property name="sampleService" ref="sampleService"/> </bean>
アノテーションベースの定義の場合はセッターに@Autiwiredを付与する
public class Sample { private SampleService service; @Autowired public void setSampleService(SampleService service){ this.service = service; } }
フィールドインジェクション
フィールドインジェクションはコンポーネントのフィールドに依存するコンポーネントをアノテーションでインジェクションする方法である。
public class Sample { @Autowired private SampleService service; }
- アノテーションを付与したフィールドと同じ型を持つコンポーネントを自動でインジェクションする
- デフォルトではインジェクションされることが必須なので、同じ型を持つコンポーネントが1つも登録されていない場合は例外が発生する
- インジェクションが必須ではない場合はrequired属性にfalseを設定すれば例外を回避でき、フィールドの値はnullとなる
- アノテーションを付与したフィールドと同じ型のコンポーネントがDIコンテナに複数登録されているときは例外が発生するので、フィールドに@Autowiredアノテーションに加え、@Qualifier アノテーションを付与し、bean名を指定することでインジェクションするコンポーネントを指定できる
- フィールドに@Qualifierが付与されなかった場合は複数存在するコンポーネントのbean定義のいずれか1つに@Primaryアノテーションを付与することでインジェクションされるコンポーネントを指定できる