📦

Spring BootのDIコンテナについて、概念や実装方法を解説

2024/02/08に公開

はじめに

Javaの代表的なフレームワークである、Spring Bootの中心的な概念である「DIコンテナ」について解説します。
開発の現場では、問題を解決するために多くのオブジェクトを組み合わせることになると思います。しかし、これらの部品の依存度が強ければ強いほど、コードはメンテナンスや拡張のしにくいものになります。それらを解決させるのが「DIコンテナ」という仕組みなのです。本記事では、「DIコンテナ」の概念から、Spring Bootにおける具体的な使用例を解説していきます。

DIコンテナを学ぶ上での前提知識

依存性注入(Dependency Injection)

依存性とは、あるオブジェクトが他のオブジェクトのメソッドやデータを使用している状態を意味します。例えば、「注文クラス」が「在庫管理クラス」にアクセスして在庫を確認する場合、注文クラスは在庫管理クラスに依存していることになります。このような依存関係にあると、注文クラスは在庫管理クラスの変更に強く影響を受けることになり、在庫管理クラスのコードを変更すると、それに伴い注文クラスも変更していかなければなりません。依存度が強いと、テストが難しくなり、柔軟性、拡張性、保守性が悪くなります。
このような問題を解決するために、依存性注入(以下「DI」とする)という手法が用いられます。DIでは、あるオブジェクトが他のオブジェクトに依存する場合、その依存オブジェクトを外部から注入することができ、これにより利用する側のオブジェクトは、利用される側のオブジェクトの具体的な実装を意識する必要がなくなるのです。

コンテナ

ソフトウェア開発におけるコンテナとは、アプリケーション内のオブジェクトの管理をしてくれる入れ物のようなものです。コンテナによって、オブジェクトの管理をコンテナに任せることによって、ユーザーはビジネスロジックの実装に集中することができます。Dockerなどが有名ですね。

DIコンテナ

DIコンテナとは

前提知識について説明したので、本題に入っていきます。DIコンテナは、アプリケーション内のオブジェクトのライフサイクル(生成から破棄)や依存関係を管理し、必要な依存オブジェクトを適切なタイミングで自動的に注入してくれます。コンテナという広い括りではDockerと同じで入れ物の役割を果たしますが、いくつかの異なる点があります。DockerはOSレベルで動作し、アプリケーション自体をパッケージ化することで、異なる環境でも同じような動作を提供できるのに対して、DIコンテナはアプリケーションのコードレベルで動作し、コードの依存関係を解消するという違いがあります。

DIコンテナの実装例

Spring BootでのDIコンテナを利用した実装例として、「注文クラス」と「在庫管理クラス」を用いて解説します。この例では、注文クラスが在庫管理クラスに依存しており、DIコンテナを通じて依存性を注入します。

在庫管理クラスの作成

在庫を管理するサービスクラスのInventoryManagimentServiceを作成します。
このサービスクラスは商品の在庫確認を行うメソッドを持ちます。

@Service
public class InventoryManagimentService {
    
    // 商品の在庫を確認するメソッド
    public boolean checkStock(String itemId) {
        // 実際の在庫確認ロジックは省略
        // ここでは、すべての商品に在庫があると仮定
        return true;
    }
}

注文クラスの作成

注文を処理するサービスクラスOrderServiceを作成します。
このサービスクラスはInventoryManagimentServiceに依存しており、注文処理の前に在庫確認を行います。

@Service
public class OrderService {

    private final InventoryManagimentService inventoryManagimentService;

    // コンストラクタを通じて依存オブジェクトを注入
    @Autowired
    public OrderService(InventoryManagimentService inventoryManagimentService) {
        this.inventoryManagimentService = inventoryManagimentService;
    }

    // 注文処理を行うメソッド
    public boolean placeOrder(String itemId) {
        // 在庫確認
        boolean isInStock = inventoryService.checkStock(itemId);

        if (isInStock) {
            // 在庫がある場合の処理(省略)
            return true;
        } else {
            // 在庫がない場合の処理(省略)
            return false;
        }
    }
}

この例では、OrderServiceクラスがInventoryManagimentServiceに依存しています。SpringのDIコンテナは、OrderServiceのインスタンスを作成する際に、InventoryManagimentServiceのインスタンスを自動的にOrderServiceに注入します。これにより、OrderServiceは在庫管理のロジックを直接見に行くことなく、InventoryManagimentServiceを利用できます。このプロセスは、依存性注入(DI)によって実現されており、コードの依存関係を解消してくれます。

まとめ

本記事では、Spring Bootの中心的な概念であるDIコンテナについて解説しました。開発の現場では、複雑な問題を解決するために多くのオブジェクトを組み合わせる必要があります。これらのオブジェクト間で強い依存関係があると、コードのメンテナンスや拡張が困難になり、このような問題を解決するのがDIコンテナです。
DIは依存関係を外部からオブジェクトに注入する機能であり、DIコンテナはそれをアプリケーション内で活用するための「機能の箱」のようなものです。我々はDIコンテナの存在を認知していなくても、開発することが可能ですが、DIコンテナの仕組みと原則を理解することで一流のソフトウェアエンジニアに近づくことができるでしょう。

参考

https://qiita.com/ritukiii/items/de30b2d944109521298f
https://qiita.com/ryohei0109_develop/items/2bcbcdf91eef928eb3bd

Discussion