概要
今回は、Java, Kotlinそれぞれを使ったSpringのDIの書き方をまとめてみます。 環境は、Java 1.8, Kotlin 1.2.31, Spring Boot 2.0.0.RELEASE, Spring 5.0.3.RELEASE です。
SpringにおけるDIについて
DIについての詳しい説明は割愛します。詳しく知りたい場合は、Springの公式リファレンスを読んでください。
SpringにおけるDIは、「Setter injection」と「Constructor injection」の主に2種類があります。ネットで調べると「Field injection」という言葉も出てきますが、公式リファレンスには出てこない用語です。説明上この記事でも、Field injectionという言葉をつかいますが、本質的にはSetter injectionと同じです。 また、Spring チームは、依存性がnon-nullであることやimmutableであることを保証できるという理由で、Constructor injectionを推奨しています。一方のSetter injectionは、依存性を再設定するケースなど、限られた用途で使うべきとされています。
以降では、これら「Field injection」, 「Setter injection」, 「Constructor injection」 それぞれについてJavaとKotlinでの書き方をまとめていきます。
Field Injection
まずは、JavaでのField Injectionについてです。 ↓のように書くことでField Injectionを表現します。
@Component class SampleComponent{ @Autowired public HogeService hogeService; @Autowired public FugaService fugaService; public void hogehoge(){ hogeService.hoge(); } public void fugafuga(){ fugaService.fuga(); } }
次にKotlinでは↓のように書きます。
@Component class SampleComponent{ @Autowired lateinit var hogeService: HogeService @Autowired lateinit var fugaService: FugaService /* 以下メソッド省略 */ }
Kotlinのフィールド変数は、宣言時に初期化されていないといけないので、DIする場合はlateinit修飾子をつけて遅延初期化であることを明示的に示す必要があります。 また、Field Injectionでは、あとから値を代入するので、valを使った変数宣言はできません。
Setter Injection
次に、setter injectionをjavaで書くと↓のようなコードになります。
@Component class SampleComponent{ private HogeService hogeService; private FugaService fugaService; @Autowired public void setHoge(HogeService hogeService){ this.hogeService = hogeService; } @Autowired public void setFuga(FugaService fugaService){ this.fugaService = fugaService; } /* 以下メソッド省略 */ }
同様に、Kotlinでsetter injectionを書くと、↓のようなコードになります。
@Component class SampleComponent{ private var hogeService: HogeService? = null @Autowired set(hogeService){ this.hogeService = hogeService } private var fugaService: FugaService? = null @Autowired set(fugaService){ this.fugaService = fugaService } /* 以下メソッド省略 */ /* ↓のように書くこともできる private lateinit var hogeService: HogeService private lateinit var fugaService: FugaService @Autowired fun setHogeService(hogeService: HogeService){ this.hogeService = hogeService } @Autowired fun setFugaService(fugaService: FugaService){ this.fugaService = fugaService } */ }
kotlinでは、フィールドをvalで宣言するとgetterが、varで宣言するとgetterとsetterが自動で生成されます。そのため、Field injection同様変数はvarで宣言します。 さらに、setterの処理をカスタムする必要があるので、↑のコードのようにset()を書く必要があります。 また、コメントに書いたように、Javaとほとんど同じ書き方で書くこともできます。
Constructor Injection
最後に、Constructor InjectionをJavaで書くと↓のようなコードになります。
@Component class SampleComponent{ private final HogeService hogeService; private final FugaService fugaService; @Autowired SampleComponent(HogeService hogeService, FugaService fugaService){ this.hogeService = hogeService; this.fugaService = fugaService; } /* 以下メソッド省略 */ }
コンストラクタがひとつしか存在しない場合は、@Autowiredを省略することができます。
@Component class SampleComponent{ private final HogeService hogeService; private final FugaService fugaService; // コンストラクタが1つしかない場合は、@Autowiredを省略できる SampleComponent(HogeService hogeService, FugaService fugaService){ this.hogeService = hogeService; this.fugaService = fugaService; } /* 以下メソッド省略 */ }
さらに、Lombokをつかってコンストラクタを自動生成すると、コンストラクタの宣言を省略してConstructor Injectionが表現できます。
@Component @AllArgsConstructor class SampleComponent{ private final HogeService hogeService; private final FugaService fugaService; // Lombokで自動生成してくれるので、コンストラクタを書かなくていい /* 以下メソッド省略 */ }
次に、KotlinでConstructor Injectionを表現すると↓のコードになります。
@Component class SampleComponent( private final val hogeService: HogeService, private final val fugaService: FugaService ){ /* 以下メソッド省略 */ }
Kotlinでは、プライマリコンストラクタという文法があり、↑のように書くことで、コンパイル時にコンストラクタを自動で生成してくれます。これによって、LombokのアノテーションをつけずにConstructor Injectionを表現することができます。