Help us understand the problem. What is going on with this article?

Java, Kotlinを使ったSpring のDependency Injectionについて

More than 1 year has passed since last update.

概要

今回は、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を表現することができます。

参考

daisuzz
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした