IoCとは何か? 〜「作る」だけでなく「呼ぶ」「片付ける」も〜


(前回記事「なぜSpring BootはDI(依存性の注入)を使うのか?」の続きです)

前回は、密結合・疎結合の違いから始まり、DI(依存性注入)がなぜ必要なのか、そしてSpringのDIコンテナ(IoCコンテナ)がどのように依存関係を自動で解決してくれるのかを解説しました。

今回は、その「DIコンテナ」のさらに奥にある設計思想――IoC(Inversion of Control/制御の逆転)について、できるだけやさしく見ていきます。

実は、DIはIoCを実現するための手段のひとつです。ざっくり言うと、DIは「必要な部品を外から渡す仕組み」、IoCは「オブジェクトの生成やフレームワークからの呼び出しの主導権をフレームワークに渡す考え方」です。SpringのDIコンテナは「IoCコンテナ」とも呼ばれます。

ただし、IoCが逆転させるのは「オブジェクトの作り方」だけではありません。 コンテナが管理するBeanでは、生成・初期化・破棄をフレームワークに任せられます。また、イベントやWebリクエスト、スケジュール実行などのフレームワークの入口では、処理を呼び出すタイミングもフレームワークが管理します。


そもそもIoCが生まれた背景

フレームワークが今ほど発達する前、エンジニアは「全部自分でやる」のが当たり前でした。

  • オブジェクトの生成は自分で new する
  • 初期化の順序は自分で管理する
  • 使い終わったリソースの解放も自分で書く
  • 処理の呼び出しタイミングも自分で決める

シンプルなアプリなら問題ありません。しかしアプリの規模が大きくなると、この「全部自分で管理する」スタイルに限界が来ます。

たとえば20個のサービスクラスがあり、それぞれが3〜5個の依存関係を持っていたら──。 初期化順序の整合性を保ち、正しく配線するだけで、本来のロジックとは無関係な膨大な作業が発生します。

ここで「制御(コントロール)を自分ではなくフレームワークに任せよう」という発想が生まれました。これがIoCです。


IoC (Inversion of Control) :「自分で管理しない」という設計思想

IoCを一言でいうと、Hollywood Principle(ハリウッドの原則)です。

“Don’t call us, we’ll call you.” (君から電話してくるな。必要なときにこちらから呼ぶから。)

このシンプルな原則が、フレームワークがどのように「制御」を握るのか、その全体像を表しています。

ここからは、DIの背景にあるIoCの本質や、DI以外にもIoCが活きる場面を、初心者向けに順番に整理していきます。

※本記事ではSpring Frameworkを例に説明しますが、.NETやAngular、NestJSなど他の多くのIoC対応フレームワークでも同様の考え方・仕組みが使われています。


IoCが管理する世界 ── DI の具体例から見える全体像

IoC以前:自分が全部やる

以下は概念を示すために簡略化したコードです。

生成→初期化→実行→破棄、すべてのライフサイクル管理が各クラスに分散し、制御が複雑になっています。

IoC採用後:Springが制御を代行

UserService は誰が自分を作るのか、いつ破棄されるのかを知りません。Springが管理するのは、Beanの生成・依存関係の解決・ライフサイクルです。一方、registerUser() のような通常のメソッドは、コントローラーや別のサービスなど、呼び出し元が明示的に実行します。


IoCが管理するもの ── DIの先にある世界

DIでカバーされるのは主に「生成」と「注入」です。IoCはそれに加えて、以下の要素も強力に管理します。

① ライフサイクル管理

Springは、コンテナが管理するBeanをいつ初期化し、いつ破棄するかを制御します。

ここでの説明は、主にデフォルトのsingleton Beanを想定しています。prototype Beanは生成後の破棄をコンテナが自動管理しないため、必要に応じて利用側で後始末を行います。

② イベントドリブンな実行

@EventListener だけで注文を自動検知するわけではありません。注文処理側が ApplicationEventPublisher などを使い、OrderPlacedEvent を発行すると、Springが対応するリスナーを呼び出します。

③ スケジュール実行

タイマー管理やスレッド制御という複雑な「制御」もフレームワークに委ねます。

スケジュール実行を使うには、設定クラスなどで @EnableScheduling を有効にします。

④ スコープ管理 ── 「何個作るか」を宣言する

オブジェクトを「毎回新しく作るのか」「同じものを使い回すのか」は、開発者がスコープとして宣言します。Springはその宣言に従ってインスタンスを提供します。

スコープ 動作 使いどころ
singleton(デフォルト) 1つのApplicationContext内で1つのインスタンスを使い回す まずはこれを覚えれば十分
prototype 取り出すたびに新しいインスタンスを作る 使い捨ての一時オブジェクト
request HTTPリクエストごとに1つ作る Webリクエスト固有の情報保持

prototypeスコープの注意点 ── 「宣言」と「取得」を分ける

prototypeスコープは「毎回新しく作る」設定ですが、singleton の bean にそのまま注入すると、最初の1回だけしか取り出されません。毎回新しいインスタンスがほしいときは、必要なタイミングで取りにいく形にします。


IoCがないとどうなるか ── 実務での痛み

IoCを使わず、すべての依存関係を自分で解決しようとすると、main 関数は「巨大な配線図」と化します。

【Before】IoCがない世界:配線だけで力尽きる

新しい部品(クラス)を追加するたびに、この main 関数の「配線図」を書き換え、依存順序に気を配る必要があります。

【After】IoCがある世界:依存関係を宣言する

IoCを採用すると、各クラスはコンストラクタで「自分はこれが欲しい」と宣言するだけになります。配線の詳細は、コンポーネントスキャンや @Bean 定義などの設定側に分離されます。

この比較からわかる通り、IoCを導入すると以下のメリットがあります:

  • 配線の詳細を分離できる:main 関数などで new を繰り返す必要がなくなります
  • 依存順序からの解放:「Aを作る前にBを作らなきゃ…」といった順序管理をSpringが自動で行います
  • 責務の集中:エンジニアは「オブジェクトの組み立て」という作業から解放され、本来の目的である「業務ロジック」の記述に集中できるようになります

まとめ:IoCが逆転させる3つの制御

制御の対象 従来のコード(主導:自分) IoC(主導:フレームワーク)
生成・注入 自分で new して渡す コンテナが作り、自動で注入する
実行タイミング 自分でメソッドを呼ぶ イベントやスケジュールなどのフレームワーク入口で呼ばれる
ライフサイクル 自分で初期化・破棄を書く アノテーションに従い自動で管理される

DIはIoCという巨大な氷山の一角に過ぎません。

「制御の流れそのものを明け渡す」ことで得られる柔軟性と保守性こそが、IoCの真価です。手放した主導権の分だけ、あなたのコードはシンプルで読みやすくなるはずです。

 

エコモットでは、Spring Bootをはじめとする最新技術を駆使して、

IoTに関連する多彩なプロジェクトに取り組んでいます。

私たちと一緒に、革新的なモノづくりに挑戦し、未来を創る仲間を募集しています。