技術関係

アノテーション付与でopenにしてくれる「kotlin-spring」の正体を見てみる

kotlin+Spring Bootを使っているとお世話になる「kotlin-spring」が何をやっているかが分からなかったので調査。

早速見てみます。これは@Controllerアノテーション付き。
「public open fun index(…」 とあります。openを明示してないのにopenになってますね。

 

 

こっちは@Controllerアノテーションなし。
「public final fun index(…」 になっています。一転、finalになりました。

画像では関数を例にしていますが、アノテーション付きのControllerにもopen修飾子が付くようになっています。
また、アノテーションが付いてないものはfinalになっています。これはKotlinのデフォルト仕様。

 

クラスに対してアノテーションを付けるとクラス、変数、関数もopen修飾子が付きます。

こんな感じでSpringのアノテーションを付けると勝手にをopenにしてくれる設定をどこでやっているかを探ります。

スポンサーリンク
スポンサーリンク

All-open compiler plugin とは?

元はこれ。
https://kotlinlang.org/docs/all-open-plugin.html

 

前述した通りクラスに対してのアノテーション付与で自動的にopen修飾子を付けてくれるすごいやつ。

また、Springを使う用途にkotlin-springというコンパイラプラグインが使える。

If you use Spring, you can enable the kotlin-spring compiler plugin instead of specifying Spring annotations manually. The kotlin-spring is a wrapper on top of all-open, and it behaves exactly the same way.

https://kotlinlang.org/docs/all-open-plugin.html#spring-support

これらのアノテーションを使うとopen修飾子が付く

kotlin-springを使用して以下のアノテーションを付与した場合、openになるよ。と書いてある。

The plugin specifies the following annotations:

  • @Component
  • @Async
  • @Transactional
  • @Cacheable
  • @SpringBootTest

Thanks to meta-annotations support, classes annotated with @Configuration@Controller@RestController@Service or @Repository are automatically opened since these annotations are meta-annotated with @Component.

Spring initializr を使ってプロジェクトを作成した場合

build.gradle.ktsの場合こんな記述がデフォルトで存在する。

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
	id("org.springframework.boot") version "2.5.6"
	id("io.spring.dependency-management") version "1.0.11.RELEASE"
	kotlin("jvm") version "1.5.31"
	kotlin("plugin.spring") version "1.5.31" // これ
}

kotlin("plugin.spring") version "1.5.31"の記述があるだけで、前述のアノテーションを付与するだけでopen修飾子が付く設定が完了している。

「all-open」みたいな記述がないので一見適用されていることが分かりづらいがこんな感じ。

他にもGradleでの書き方は色々あるので参考に
https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.spring

メタアノテーションってなんぞや

openにすることが出来るアノテーション一覧で見たこんな記述。

Thanks to meta-annotations support, classes annotated with @Configuration@Controller@RestController@Service or @Repository are automatically opened since these annotations are meta-annotated with @Component.

要は@Component使わなくても@Serviceとか@RestControllerでもopenにすることが出来るよ!という話。

これを実現しているのはメタアノテーションのおかげ、と書いてあるが果たしてなんだろうか。

メタアノテーション = アノテーションに指定出来るアノテーション

メタアノテーションはアノテーションに指定出来るアノテーションのこと。
Springは複数のアノテーションを組み合わせた独自カスタムアノテーションを多く実装している。

@Component

Componentアノテーションには @Targetと@Retention、@Documented、@Indexdのアノテーションが付与されている。
この複数の組み合わせによってComponentアノテーションが作られている。

https://github.com/spring-projects/spring-framework/blob/5.3.x/spring-context/src/main/java/org/springframework/stereotype/Component.java#L46

@Controller

一見別物のように見えるControllerアノテーションには@Componentのアノテーションが付与されている。

ここでは紹介しないが、@Service@Repository@Configurationなども同様に@Componentアノテーションが付与されている。

https://github.com/spring-projects/spring-framework/blob/5.3.x/spring-context/src/main/java/org/springframework/stereotype/Controller.java#L53

@RestController

一方、RestControllerを見てみる。
@Controllerアノテーションと@ResponseBodyがあるのがわかる。

なるほどなるほど、すべては繋がっている。

https://github.com/spring-projects/spring-framework/blob/5.3.x/spring-web/src/main/java/org/springframework/web/bind/annotation/RestController.java#L61

@AliasFor でパラメータも渡せる

上記の記述を見ると@AliasFor でアノテーションクラスを指定しているが、この記述でvalueパラメータも引数のannotationで指定してあげることでアノテーションに値を引き渡す事が出来る。まさにエイリアス。

使い方に関してはこの記事が詳しく書かれてました。

Springのアノテーションを合成してプロジェクト固有のステレオタイプを作る / @kuinaein

アノテーションの組み合わせ夢が広がる

と、言うわけでopen修飾子を付与する事が出来るアノテーションは下記の通りですが、

  • @Component
  • @Async
  • @Transactional
  • @Cacheable
  • @SpringBootTest

メタアノテーションの機能を使って@Configuration@Controller@RestController@Service@Repositoryを使った場合でも、open修飾子が付与されるということです。

環境

  • Kotlin v1.5.31
  • Spring Boot 2.6.0

参考

All-open compiler plugin

Kotlin Compiler Plugin (kotlin-allopen) を追いかける / らびたろちゃんにっき

Springのアノテーションを合成してプロジェクト固有のステレオタイプを作る / @kuinaein

メタアノテーション

org.jetbrains.kotlin.plugin.spring / gradle

https://www.intertech.com/spring-4-meta-annotations / INTERTECH Spring 4 Meta Annotations

コメント

タイトルとURLをコピーしました