SpringにおけるDI(依存注入)とアノテーションの解説


Springでの例

HogeInterfaceとそれを実装したHogeLocalを考える。
中身のメソッドはなんでも良い。
Springの場合、以下のように書けばprofileを"local"、"production"にスイッチするだけで簡単に切り替え可能

// インターフェースの部分
public interface HogeInterface {

    String hogePrint(); // 返り値は書かない
}
@Component //このアノテーションをつけとけば必要に応じてDIで注入してくれる
@Profile({"local"}) // localの場合
public class HogeLocal implements HogeInterface {

    @Override
    public String hogePrint() {
        return "localだよ";
    }
}
@Component
@Profile({"production"}) // productionの場合
public class HogeProduction implements HogeInterface {

    @Override
    public String hogePrint() {
        return "productionだよ"; // localの場合とは違う処理
    }
}
// 勝手に注入してくれるので、localの場合もproductionの場合もここを書き換える必要なし
@Autowired // このアノテーションつけとけば@conmponentから適当なやつを注入してくれる
    public Fuga(final HogeInterface hoge) { //ここに書くのはインターフェースだけ
        system.out.println(hoge.hogePrint);
    }

解説

上記のようにDIを行わないと以下のように書かなきゃいけない。


public Fuga() { 
    HogeInterface hoge = new HogeLocal();
    // HogeInterface hoge = new HogeProduction();

    system.out.println(hoge.hogePrint);
}

newをする実装部分を必要に応じてコメントアウトしなきゃけないので面倒。
また、実装に依存してしまっているのでよろしくない。
図で考える。

悪い例だと上流であるFugaが下流に依存してしまっている。
HogeLocalHogeProductionの詳細部分の仕様が変更になった場合、依存ているFugaにも変更を加えなきゃいけなくなる。

正しい例だとFugaHogeInterfaceにしか依存していないため、HogeLocalやHogeProductionの仕様が変更になってもHogeInterfaceの形式さえ守ってればFugaは改修の必要がない。
ここで、すべての矢印が抽象であるInterfaceに向かっていることに注目したい。
DIを使えばInterfaceという抽象にすべてのクラスが依存するため疎結合にすることができる。

また、正しい例では上位->下位への依存ではなく、下位->上位に依存の矢印が向いていることがわかる。
これを依存関係逆転という。

アノテーションの説明

今回のコードで出てきてないアノテーションも含め、よく使うものをまとめてメモ。

Spring独自のアノテーション

これをつけておくとComponentの中から適当なやつを注入してくれる

クラスにつけるアノテーション。これをつけたクラスはSpringの監視下に置かれ、注入の対象になる

@Componentを継承しており、使い方はほぼ@Componentと同じ。
監修的にContoller、Service、Repositoryそれぞれの機能に応じてつける

環境ごとに設定を変えたい場合などにprofileを切り替える。
このアノテーションをつける言で、環境ごとに自動的に読み込むファイルを変更してくれる。
profilesはspringの実行時の引数としてspring.profiles.activeで指定する。
Mavenの場合はpom.xmlで指定しておけばよしなに設定してくれる。

  • @Data
    @Getter/@Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructorをまとめて付与したのと同じになる。

  • @Getter

    • 勝手にgetterメソッド作ってくれる。便利
  • @Setter

    • 勝手にsetterメソッド作ってくれる。便利
  • @ToString

    • 勝手にToStringメソッド作ってくれる。便利
  • @EqualsAndHashCode
      - クラス内の値が同じならhashコードも同じになる
      - つまり、簡単にクラスどうしの比較ができる

  • @RequiredArgsConstructor

    • 必須 (finalついてるやつ)を対象にしたコンストラクタを自動生成してくれる
  • @AllArgsConstructor

    • 全部の変数のコンストラクタを自動生成してくれる
  • @NoArgsConstructor

    • 引数なしコンストラクタを自動生成してくれる
  • @Scope

    • どの単位でインスタンスを作成するかを決める
    • デフォルトはsingletonで、DIコンテナに対して1つだけインスタンスを作成
    • prototypeにするとアクセス時に毎回インスタンスを作成
    • request、sessionはそれぞれHTTPリクエスト、セッション単位
  • @Bean

    • DIコンテナに登録する
    • @Componentで登録したHogeクラスの場合は"Hoge"がbean名になる
    • Autowiredとか使った場合はいちいちBeanを使わなくてもよい
  • @Primary

    • 同じタイプのBeanが複数ある場合はPrimaryが付いているものを優先する
  • @Qualifier

    • 同じ方のBeanが複数与えられている場合は固有名を与える
  • @NestedConfigurationProperty

  • @ConfigurationProperties

Javaにおけるアノテーション

実装 (implement)した場合、元の抽象クラスの関数を実装した関数から使うためのアノテーション。
Springに限らずよく使う。
実装、継承したクラスごとに異なる動作が可能なので、これをポリモーフィズム (polymorphism) と呼ぶ。