逆引きMicronaut

MicronautフレームワークでのRestful API実装を試してみた際に調べたことを整理した。（作成したプロジェクトは[micronaut-rest-api](https://github.com/unhurried/micronaut-rest-api/)に置いている。）

#### 公式ガイドライン・リファレンス

* Micronaut本体：[User Guide](https://docs.micronaut.io/latest/guide/)　[API Reference](https://docs.micronaut.io/latest/api/)
* 周辺ライブラリ（Micronaut Dataなど）：[User Guide一覧](https://micronaut.io/docs/)

#### 開発環境 (InteliJ) セットアップ・プロジェクト作成

* Micronaut CLIをインストールする。Windows環境には[Chocolatey](https://micronaut-projects.github.io/micronaut-starter/latest/guide/#installChocolatey)でインストールできる。
* [mnコマンド](https://micronaut-projects.github.io/micronaut-starter/latest/guide/#commands)でプロジェクトを新規作成する。

	```cmd
	mn create-app --build=gradle_kotlin --lang=kotlin --jdk=11 --test=junit --features=jax-rs io.github.unhurried.micronaut-rest-api
	```

* mnコマンドを使う方法の他にも[Micronaut Launch](https://micronaut.io/launch)でプロジェクトを作成してダウンロードすることもできる。（参考：[Kotlinベースのプロジェクト設定](https://micronaut.io/launch?type=DEFAULT&name=demo&package=com.example&javaVersion=JDK_11〈=KOTLIN&build=GRADLE_KOTLIN&test=JUNIT&features=jax-rs&version=3.1.3)）
* InteliJ IDEAにインポートしてアノテーションプロセッサを有効化する。（参考：[2.3 Setting up an IDE](https://docs.micronaut.io/latest/guide/#ideaSetup)）

#### 開発用にアプリケーションを（デバッグ）実行する

* `View -> Tool Windows -> Gradle -> applicaition / run` から（デバッグ）実行すればよい。

#### Dependency Injection

* Micronautはコンパイル時のデータを利用して、リフレクションやプロキシにできる限り頼らない方法でDIを実装しているとのこと。起動時間短縮やメモリ使用量削減のメリットがある。（参考：[3 Inversion of Control](https://docs.micronaut.io/latest/guide/#ioc)）
* JSR-330 (javax.inject) の実装となっているため@Namedや@Injectを使った設定が可能な上に、他のDIコンテナ同様に独自のスコープ定義も用意されている。（参考：[3.7.1 Built-In Scopes](https://docs.micronaut.io/latest/guide/#builtInScopes)）

#### Restful APIを実装する

* MicronautではHTTPサーバーとして動かすための独自のAPIが提供されている。HTTPサーバーにはNettyが使われている。（参考：[6 The HTTP Server](https://docs.micronaut.io/latest/guide/#httpServer)）
* 他のフレームワークと同様に、[@Controller](https://docs.micronaut.io/latest/api/io/micronaut/http/annotation/Controller.html)や[@Get](https://docs.micronaut.io/latest/api/io/micronaut/http/annotation/Get.html)などのアノテーションを使ってコントローラクラスを実装できる。
* エラー処理は例外もしくはステータスコードに対してエラーハンドラを定義する。エラーハンドラはコントローラごと（ローカルエラーハンドラ）もしくはグローバルの2種類がある。（参考：[6.17 Error Handling](https://docs.micronaut.io/latest/guide/#errorHandling)）
* Restful APIのベースパスを設定するには、micronaut.server.context-pathを設定すればよい。（参考：[Configuration Reference](https://docs.micronaut.io/latest/guide/configurationreference.html)）   
* [Micronaut JAX-RS](https://micronaut-projects.github.io/micronaut-jaxrs/latest/guide/)を使うとJAX-RS APIもある程度使える。
	* Micronaut JAX-RSはJAX-RS実装ではなく、あくまでよく使われるAPIをMicronautで使えるようにすることが目的とのことで、全てのAPIがサポートされている訳ではない。例えば、JAX-RSアノテーションは、実際にはコンパイル時にMicronautのアノテーションに置き換えることで対応されている。
	* サポートされないAPIと回避策
		* @BeanParamがサポートされていないため、クエリ・ヘッダパラメータをクラスにまとめることができない。リソースメソッドの引数として宣言する必要がある。
		* WebApplicationExceptionをレスポンスに変換する処理が実装されていないので、自力でエラーハンドラを用意する必要がある。    
	* Jerseyなどの別のJAX-RS実装をサポートする予定はなさそう。（参考：[#4896](https://github.com/micronaut-projects/micronaut-core/issues/4896), [#4942](https://github.com/micronaut-projects/micronaut-core/discussions/4942)）
* 実装例：[ToDoResource.kt](https://github.com/unhurried/micronaut-rest-api/blob/v0.1/src/main/kotlin/io/github/unhurried/micronautrestapi/resource/ToDoResource.kt), [ErrorHandler.kt](https://github.com/unhurried/micronaut-rest-api/blob/v0.1/src/main/kotlin/io/github/unhurried/micronautrestapi/resource/ErrorHandler.kt)

#### HTTPリクエスト・レスポンスを操作する

* [HttpRequest](https://docs.micronaut.io/latest/api/io/micronaut/http/HttpRequest.html), [HttpResponse](https://docs.micronaut.io/latest/api/io/micronaut/http/HttpResponse.html)などの抽象化されたAPIが用意されているためこれらを使えばよい。（参考：[6.9 The HttpRequest and HttpResponse](https://docs.micronaut.io/latest/guide/#requestResponse)）

#### データベースアクセスを実装する

* JDBC・JPAサポート（Micronaut SQL）
	* MicronautではJDBCもしくはJDBC + JPA (Hibernate) によるデータベースアクセスがサポートされている。（参考：[Micronaut 12.2 Cofigurations for Data Access](https://docs.micronaut.io/latest/guide/#dataAccess)、[Micronaut SQL）](https://micronaut-projects.github.io/micronaut-sql/latest/guide/)
	* JDBC（コネクションプール）実装はAapache DBCP2, Hikari, Tomcatの3つの実装から選択できる。
	* データベースアクセス機能を利用するには、JDBC実装とJDBCドライバの依存関係（JPAを使う場合はHibernateの依存関係も）を設定すればよい。

		```kotlin
		// Hikari + H2 Databaseの例
		runtime("io.micronaut.sql:micronaut-jdbc-hikari")
		runtime("com.h2database:h2")
		// JPAを利用する場合は以下も追加する
		implementation("io.micronaut.sql:micronaut-hibernate-jpa")
		```

	* データソースはapplication.yamlに設定する。（参考：[Configuring JDBC Connection Pools](https://micronaut-projects.github.io/micronaut-sql/latest/guide/#_configuring_jdbc_connection_pools)）

* Micronaut Data
	* JDBC・JPAサポートに加えて、[Micronaut Data](https://micronaut-projects.github.io/micronaut-data/latest/guide/)という関連プロジェクトがあり、GORMやSpring Dataに似たAPIが提供されている。AoTによるリフレクションに頼らない仕組みとのこと。
	* Micronaut Dataを使うにはJDBC実装とJDBCドライバに加えて以下の依存関係を設定すればよい。

		```kotlin
		kapt("io.micronaut.data:micronaut-data-processor")
		// JPAを利用する場合は以下を設定する
		implementation("io.micronaut.data:micronaut-data-hibernate-jpa:3.1.2")
		```

	* 実装例：[ToDoRepository.kt](https://github.com/unhurried/micronaut-rest-api/blob/v0.1/src/main/kotlin/io/github/unhurried/micronautrestapi/repository/ToDoRepository.kt), [ToDo.kt](https://github.com/unhurried/micronaut-rest-api/blob/v0.1/src/main/kotlin/io/github/unhurried/micronautrestapi/repository/entity/ToDo.kt)

* トランザクション境界の設定
	* [@Transactional](https://jakarta.ee/specifications/platform/8/apidocs/javax/transaction/transactional) (JTA) もしくはSpring Transactionによるトランザクション境界の設定ができる。（参考：[6.6 Transactions](https://micronaut-projects.github.io/micronaut-data/latest/guide/#transactions)）

* その他の参考資料
	* [ACCESS A DATABASE WITH JPA AND HIBERNATE](https://guides.micronaut.io/latest/micronaut-jpa-hibernate-gradle-java.html)

#### Bean (DTO, data class）のプロパティをコピーする

* [Apache Commons](https://commons.apache.org/proper/commons-beanutils/)や[Spring Framework](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html)のBeanUtilsのように、型の異なるオブジェクト間でプロパティをコピーするユーティリティを実装する。
* Micronautではリフレクションに頼らずにBeanのプロパティの読み書きなどの処理を実装するために[Bean Introspection](https://docs.micronaut.io/latest/guide/index.html#introspection)という機能が提供されている。
	* 低レイヤのI/Fである[BeanIntrospector](https://docs.micronaut.io/latest/api/io/micronaut/core/beans/BeanIntrospector.html)/[BeanIntrospection](https://docs.micronaut.io/latest/api/io/micronaut/core/beans/BeanIntrospection.html)に加えて、抽象化された[BeanWrapper API](https://docs.micronaut.io/latest/api/io/micronaut/core/beans/BeanWrapper.html)も用意されている。
* Bean Introspectionを利用するには、以下の依存関係を設定すればよい。

	```kotlin
	kapt("io.micronaut:micronaut-inject-java")
	runtimeOnly("io.micronaut:micronaut-core")
	```

* 実装例：[BeanHelper.kt](https://github.com/unhurried/micronaut-rest-api/blob/v0.1/src/main/kotlin/io/github/unhurried/micronautrestapi/helper/BeanHelper.kt)
* その他の参考資料
	* https://gitter.im/micronautfw/questions?at=5f19c7fe261ed758ea2321be

#### Aspected Oriented Programmingを実装する

* Micronautではリフレクションに頼らないcompile-time AOP機能が提供される。（参考：[5 Aspect Oriented Programming](https://docs.micronaut.io/latest/guide/#aop)）
* コンパイル時にAspectを組み込むクラスのサブクラスを生成する仕組みになっているため、クラスがデフォルトでfinalとなるKotlinではopenを指定する必要がある。これを自動化できるall-openプラグインも提供されているがメソッドには効かないため、メソッドにAOPを設定する場合はopenの指定が必要となる。（参考：[14.3.3 Kotlin and AOP Advice](https://docs.micronaut.io/latest/guide/#openandaop)）
* 実装はシンプルで、AOP設定用のアノテーションと、それに対応するAdvice（インターセプタ）クラスを実装すればよい。 ※ AspectJのように適用対象をAdviceクラスに記述するAPIは提供されていない。
* 実装例：[LoggerAdvice.kt](https://github.com/unhurried/micronaut-rest-api/blob/v0.1/src/main/kotlin/io/github/unhurried/micronautrestapi/aspect/LoggerAdvice.kt), [LoggerAspect.kt](https://github.com/unhurried/micronaut-rest-api/blob/v0.1/src/main/kotlin/io/github/unhurried/micronautrestapi/aspect/LoggerAspect.kt)

#### ログを出力する

* Slf4jが組み込まれているためLoggerFactoryを使ってクラスに対応するロガーを取得すればよい。（参考：[Logging](https://docs.micronaut.io/latest/guide/#logging)）

#### テストコードを実装する

* Micronaut Testプロジェクトとしてテスト用の機能が提供されている。Spock, JUnit5, Kotest, Kotlin Testそれぞれの拡張として提供されるため、好きなテストフレームワークを選択できる。
* EmbeddedServerインスタンスをDIすることでテスト用にアプリケーションを起動することもできる。
* 実装例：[MicronautRestApiTest.kt](https://github.com/unhurried/micronaut-rest-api/tree/v0.1/src/test/kotlin/io/github/unhurried/micronautrestapi)