IoCとDIの概念の総括


DI(Dependency Injection)
  • 依存性:あるクラスが別のクラスを作成する必要がある場合、そのクラス間に依存性があることを示す
  • 次のコードでは、CarクラスにはTireクラスが必要であるため、CarクラスにはTireクラスの依存性があるといえる
  • 一般依存関係は新キーワードの宣言と密接に関連している
  • class Car{
        Tire tire;
        
        public Car(){
            this.tire = new Tire();
        }
    }
  • 依存注入は直接オブジェクトを生成するのではなく、外部からオブジェクトを作成して注入する方式
  • モジュール間の結合度が低いため柔軟性が高い
  • 上記コードでは、CarクラスとTireクラスの結合度が高いため、あるクラスを修正すると別のクラスを修正する手間がかかる
  • したがって結合度が低い(=依存性または依存性が小さい)ほどコードの再利用性と柔軟性が高くなる
  • 注入依存性
  • ジェネレータまたはsetterメソッドを使用して依存性を注入可能
  • 上記のようにnewによる直接生成ではなく、外部生成の対象を導入する
  • class Car{
        Tire tire;
        
        public Car(Tire tire){
            this.tire = tire;
        }
        
        public void setTire(Tire tire){
            this.tire = tire;
        }
    }
  • スプリングフレームは依存対象をbeanと呼び、最初のプロジェクト実行時にbeanを作成しbeanコンテナで管理し、必要に応じてbeanをコンテナから注入して利用する
  • 開発者主導ではなくフレームワークによるbeanオブジェクトの作成と破棄
  • Singleton Patternの特徴が現れる
  • IoC(Inversion of Conrol)

  • 制御反転:開発者がメソッドまたはオブジェクトの呼び出しを決定するのではなく、フレームワークによって決定されます.

  • 前述したようにSpringのbean(依存オブジェクト)は開発者ではなくフレームワークによって作成され破棄され、開発者はコンテナのフレームワークによって作成されたオブジェクトを呼び出して使用します.

  • スプリングでbeanを作成および実行する手順は、次のとおりです.

  • 初期オブジェクトを作成します.

  • 依存オブジェクトを注入します.
  • フィールドインスタンス、setterまたはコンストラクション関数等による注入
  • @Autowired宣言、枠を見つけて容器に入れ、宣言の対象タイプに合うbeanを注入

  • 依存オブジェクトメソッドが呼び出されます.

  • 通常、依存オブジェクトを注入する場合、スプリングは作成者に推奨されます.
  • 参考:https://reflectoring.io/constructor-injection/
  • 外部からのアクセスが容易、テストコードの作成が容易
  • 最も重要なのは、循環引用による誤りを自発的に防止すること
  • 下図のように、2つのbeanが互いに依存している場合、1つのbeanが内務のcall()メソッドを呼び出すとcalstackが蓄積し続けるStackOverflowError発生
  • Calling Cat->Calling Cat->Calling Dogが無限に繰り返される場合…
  • 問題は、これらの異常はコンパイルエラーではなくランタイムエラーであるため、メソッドが実際に呼び出されるまでループ参照の部分を見つけることが困難である
  • @Component
    public class Dog{
        @Autowired
        private Cat cag;
        
        public void call(Car cat){
            System.out.println("Calling Cat");
            cat.call();
        }
    }
    
    @Component 
    public class Cat{
        @Autowired
        private Dog dog;
        
        public void call(Dog dog){
            System.out.println("Calling Dog");
            dog.call();
        }
    }
  • コンストラクタ注入を使用する場合、コンパイル時に循環参照エラーを解消することを推奨
  • 生成者注入は通常1つ目の生成者呼び出し時に1回呼び出すことを保証するので、ループリファレンスが発生した場合BeanCurrentlyInCreationException、予め問題が発生しないようにすることができる
  • @Component
    public class Dog{
        
        private Cat cag;
        
        @Autowired
        public Dog(Car cat){
            this.cat = cat;
        }
        
        public void call(Car cat){
            System.out.println("Calling Cat");
            cat.call();
        }
    }
    
    @Component 
    public class Cat{
        
        private Dog dog;
        
        @Autowired
        public Cat(Dog dog){
            this.dog = dog;
        }
        
        public void call(Dog dog){
            System.out.println("Calling Dog");
            dog.call();
        }
    }
  • 普通は個々の生成者しか存在しない・使用不要@Autowiredただし2つ以上あれば必ず協力を宣言!