DIコンテナとは何か
DIコンテナは、オブジェクト間の依存関係を管理する仕組みです。
インスタンスの生成、オブジェクトへのインスタンスの注入、インスタンスのライフサイクルの管理の役割を担います。
なぜDIコンテナを使うのか
従来
public class UserService {
public List<User> findAll() {
UserRepository userRepository = new JdbcUserRepository();
return userRepository.selectAll();
}
}
問題点
JdbcUserRepositoryクラスをテスト用のTestUserRepositoryに変更したい場合、以下のように変更する必要があります。
public class UserService {
public List<User> findAll() {
//JdbcUserRepository → TestUserRepository
UserRepository userRepository = new TestUserRepository();
return userRepository.selectAll();
}
}
この状態では実行する状況に応じてコードを書き換える必要があり、保守性が低いコードとなってしまいます。
DIを使った解決
※DIの方法は複数ありますが、今回はコンストラクタインジェクションを用います。
従来の書き方では、new 演算子で利用するオブジェクトを呼び出していましたが、DIを使用する場合、コンストラクタの引数で利用したいオブジェクトの型を指定することで、その型のオブジェクトをフィールドに代入しています。
このようにオブジェクトを型を指定して受け取ることで、従来の書き方のように本番用とテスト用でコードの書き換えの必要が無くなり、保守性を高めることができます。
public class UserService {
private final UserRepository userRepository;
//UserRepositoryインターフェースの具象クラスであれば、JdbcUserRepositoryクラスでも、TestUserRepositoryでも注入することが可能
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<User> findAll() {
return userRepository.selectAll();
}
}
DIコンテナはどう動くか
-
コンポーネントスキャン
JavaConfigクラスに@ComponentScanアノテーションを記述し、アプリケーション起動時にステレオタイプアノテーション(@Component、@Service)のついたオブジェクトやJavaConfigクラス内の@Beanアノテーションのついたメソッドを探します。 -
Beanインスタンス生成
コンポーネントスキャンで見つけたステレオタイプアノテーションのついたオブジェクトや@Beanメソッドの戻り値となるオブジェクトのインスタンスを生成します。このDIコンテナが管理するインスタンスのことをBeanと言います。 -
注入先の検索
@Autowiredアノテーションのついたフィールド、コンストラクタ、メソッドを探します。
※コンストラクタが1つしかないクラスの場合、@Autowiredアノテーションを省略可能です。public class UserService { private final UserRepository userRepository @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } }
-
インジェクション(注入)の実行
見つけた注入先にBeanを渡します。
上記のコードでは、コンストラクタの引数に「UserRepository」型を指定しています。
DIコンテナはBean(DIコンテナが管理するオブジェクト)コンストからコンストラクタの引数の型のサブクラスや具象クラスを注入先に渡します。