インターフェイスと DI
サービスとリポジトリ で組み立てた 3 層は、BookRepository をインターフェイスにし、DI コンテナーが実装を注入する形にできます。
1. インターフェイスへの依存
Section titled “1. インターフェイスへの依存”BookService は、データアクセスの実装クラスである BookRepository に直接依存しています。インターフェイス では、GradeJudge をインターフェイスに変えて実装クラスを分けると、利用側を変えずに実装を差し替えられると述べました。BookRepository にも同じ形を適用します。
1-1. インターフェイスと実装の分離
Section titled “1-1. インターフェイスと実装の分離”メソッドの宣言だけを BookRepository インターフェイスとして残し、JDBC の実装は JdbcBookRepository に分けます。
package com.example.bookstore;
import java.util.List;
public interface BookRepository { List<Book> findAll(); void save(Book book); boolean existsByTitle(String title);}@Repositorypublic class JdbcBookRepository implements BookRepository { private final DataSource dataSource;
public JdbcBookRepository(DataSource dataSource) { this.dataSource = dataSource; }
// findAll・save・existsByTitle の本体は、インターフェイスにする前の BookRepository と同じ}@Repository は実装クラスの JdbcBookRepository に移ります。implements BookRepository と各メソッドへの @Override を加えるだけで、メソッドの本体は変わりません。BookService のフィールドの型は BookRepository(インターフェイス)のままで、BookService のコードは修正不要です。
1-2. 実装の注入
Section titled “1-2. 実装の注入”BookService が受け取るのは BookRepository 型ですが、Spring Boot が注入するのは実装の JdbcBookRepository です。インターフェイス型のフィールドに実装のインスタンスを代入できるのは、多態性 によるものです。
2. DI コンテナー
Section titled “2. DI コンテナー”new を書かずに実装が注入されるのは、Spring Boot の DI コンテナーが組み立てを担うためです。
2-1. Bean の登録と注入
Section titled “2-1. Bean の登録と注入”Spring Boot が生成・管理するオブジェクトを Bean と呼びます。@Controller・@Service・@Repository の付いたクラスは、起動時に Bean として登録されます。Bean を保持し、依存を解決して注入する仕組みを DI コンテナー(ApplicationContext)と呼びます。
起動時、コンポーネントスキャンが Bean を見つけてコンテナーに登録します。コンテナーは各 Bean のコンストラクターの引数をたどって依存関係を調べ、必要な Bean から順に生成して注入します。Bean は既定で 1 つだけ生成され、アプリケーション全体で共有されます。
2-2. 実装の差し替え
Section titled “2-2. 実装の差し替え”BookService のコードには、BookRepository(インターフェイス)の型だけが現れ、実装クラスの名前は現れません。そのため、コンテナーに登録する実装を入れ替えれば、BookService を変えずに取得方法を変えられます。たとえばテストでは、データベースにアクセスせずメモリ上のリストで動く実装を渡せば、BookService の検証ルールだけを、データベースを起動せずに素早く確かめられます。
どの実装を注入するかを外から決められることが、依存性の注入の利点です。これは インターフェイス で扱った実装の差し替えを、DI コンテナーが行う仕組みです。