SpringFrameworkーDIとは?

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