概要
今回は、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 val hogeService: HogeService,
private val fugaService: FugaService
){
/* 以下メソッド省略 */
}
Kotlinでは、プライマリコンストラクタという文法があり、↑のように書くことで、コンパイル時にコンストラクタを自動で生成してくれます。これによって、LombokのアノテーションをつけずにConstructor Injectionを表現することができます。