DI (Dependency Injection) って何?


Spring Bootを使って開発をしていると、よく耳にする「DI」という言葉。Dependency Injection、日本語で「依存性注入」と訳されます。一体これは何なのでしょうか?
初心者の方にとっては、少しとっつきにくい概念かもしれません。この記事では、DIの概念を分かりやすく説明し、Spring BootにおけるDIの役割を理解して頂けるよう解説します。

まず、DIとは何か簡単に説明すると、「あるオブジェクトが必要とする依存関係を、外部から注入する」という設計手法です。

依存関係とは?

DIを理解する上で重要なのは「依存関係」という考え方です。Javaのような言語でプログラムを記述する際には、複数のクラスが相互に作用し合います。例えば、ユーザー情報を管理するクラスが、データベースにアクセスするクラスに依存している、といった具合です。この「あるクラスが別のクラスの機能に頼っている」という関係が「依存関係」です。

従来のプログラミングでは、依存関係のあるクラスを直接インスタンス化することが多くありました。例えば、ユーザー情報管理クラスがデータベースアクセスクラスのインスタンスをnewを使って作成していました。

具体的には以下のような感じです。

// ユーザー情報管理クラス
public class UserManagementService {
    // データベースアクセスクラスのインスタンスを作成
    private DatabaseAccessRepository db = new DatabaseAccessRepository();

    public void doSomething() {
        db.findUserById(1);
    }
}
// データベースアクセスクラス
public class DatabaseAccessRepository {
    public void findUserById(int id) {
        // データベースからユーザー情報を取得する処理
    }
}

もちろん、このような実装でも問題なく動作します。しかし、このような実装は、密結合と呼ばれ様々な問題を引き起こします。例えば、データベースの種類を変更したい場合、ユーザー情報管理クラスのコードも修正する必要が出てきます。 テストを行う場合も、実際にデータベースに接続する必要があり、煩雑になります。

そこで登場するのがDIです。DIとは、依存関係にあるクラスのインスタンスを、外部から「注入」する設計手法です。具体的には、ユーザー情報管理クラスがデータベースアクセスクラスのインスタンスを自分で作らず、外部から受け取るようにします。この「外部から与える」という行為が「注入」です。

こうすることにより、ユーザー情報管理クラスはデータベースアクセスクラスの実装の詳細を知ることなく、その機能を利用できるようになります。データベースの実装を変更したい場合も、ユーザー情報管理クラスのコードを変更する必要はありません。 (実際に実装を切り替えるには、interfaceなどの理解が必要になります)

Spring BootにおけるDI

Spring Bootでは、この注入をコンテナと呼ばれる仕組みが担っています。Springコンテナは、様々なクラスのインスタンスを管理し、必要な時に適切なインスタンスを注入してくれます。これにより、クラス間の依存関係を弱くし、保守性やテスト容易性を向上させることができます。

Spring BootにおいてDIを実現するには@Autowiredアノテーションを使います。@Autowiredアノテーションをフィールド変数、コンストラクタ、setterメソッドに付けることで、Spring Bootが自動的に依存関係のあるクラスのインスタンスを注入してくれます。

以下に具体的な例を示します。

// ユーザー情報管理クラス
@Component
public class UserManagementService {
    private final DatabaseAccessRepository db;

    // コンストラクタに@Autowiredアノテーションを付ける
    @Autowired
    public UserManagementService(DatabaseAccessRepository db) {
        this.db = db;
    }

    public void doSomething() {
        db.findUserById(1);
    }
}
// データベースアクセスクラス
@Component
public class DatabaseAccessRepository {
    public void findUserById(int id) {
        // データベースからユーザー情報を取得する処理
    }
}

この例では、@Componentアノテーションで2つのクラスをSpringコンテナに登録しています。@Autowiredアノテーションをコンストラクタに付けることで、UserManagementServiceクラスにDatabaseAccessRepositoryクラスのインスタンスが自動的に注入されます。これにより、UserManagementServiceクラスはDatabaseAccessRepositoryクラスのインスタンスを自分で作成する必要がなくなります。

DIのメリット

DIを使うことで、クラス間の結合度を低く保ち、テスト容易性や保守性を向上させることができます。また、DIを使うことで、コードの再利用性も高まります。依存関係のあるクラスを直接インスタンス化すると、そのクラスを使いたい度に新たにインスタンスを作成する必要がありますが、DIを使うと、外部からインスタンスを受け取るため、同じインスタンスを再利用することができます。

例えば、DatabaseAccessRepositoryクラスの実装が変更された場合、UserManagementServiceクラスのコードを修正する必要はありません。また、DatabaseAccessRepositoryクラスのテストを行う際にも、実際のデータベースに接続することなく、テスト用のモックオブジェクトを注入することで、簡単にテストを行うことができます。

まとめ

DIはクラス間の依存関係を管理し、保守性やテスト容易性を向上させるための強力な設計手法です。
Spring Bootでは、このDIが中心的な役割を果たしており、その恩恵を最大限に活用することで、より効率的でメンテナンス性の高いアプリケーション開発が可能になります。
目に見えない部分で依存関係がうまく管理されているおかげで、開発者はビジネスロジックの実装に集中でき、より高品質なアプリケーションを開発することができるのです。

DIについて理解することで、Spring Bootの開発においてよりスムーズに作業を進めることができるようになるでしょう。ぜひ、DIの概念をしっかりと理解し、Spring Bootの開発に活かしてみてください。