MicronautでDIしてみる

# MicronautでDI
Micronautには、DI（Dependency Injection）の仕組みがあるらしいです。

[Inversion of Control](https://docs.micronaut.io/1.0.4/guide/index.html#ioc)

特徴としては、

* コンパイル時に生成するデータを使用する（Annotation Processorを利用）
* リフレクションとプロキシを避ける
* 起動時間を最適化する
* メモリフットプリントを減らす
* 明快で、エラーハンドリングをわかりやすくする

といったものがあるようです。


# MicronautのDIで使うスコープ

Micronautでは、アノテーションにJSR-330のものを使用します。

https://docs.oracle.com/javaee/6/api/javax/inject/package-summary.html

JSR-330に含まれる`@Singleton`に加えて、Micronautで利用するスコープアノテーションは、こちら。

[Scopes](https://docs.micronaut.io/1.0.4/guide/index.html#scopes)

* `@Singleton`
* `@Context`
* `@Prototype`
* `@Infrastructure`
* `@ThreadLocal`
* `@Refreshable`

いくつか、変わったものがありますね。

`@Context`スコープというのは、`BeanContext`の起動、終了時に初期化、シャットダウンされる必要があるBeanを表します。 
※`BeanContext`というのは通常`ApplicationContext`で、DIのエントリーポイントとなるものです

Micronautでは、SingletonなBeanを遅延初期化するようなので、`@Context`アノテーションを使用すると`BeanContext`の起動時に初期化することができるようになります。とはいえ、Micronautは起動時に最小限のBeanを作成するように設計されているので、`@Context`の仕様は控えめに、ということらしいですが。

`@ThreadLocal`とか、変わったものもありますね。`@Infrastructure`は、`@Singleton`のステレオタイプみたいです。

また、リフレッシュ可能な`@Refreshable`というスコープもあり、`/refresh`エンドポイントや`RefreshEvent`によるリフレッシュができるようです。

[Refreshable Scope](https://docs.micronaut.io/1.0.4/guide/index.html#refreshable)

このあたりのアノテーションがあるパッケージは、こちら。

https://docs.micronaut.io/1.0.4/api/io/micronaut/context/annotation/package-summary.html

とはいえ、[Quick Start](https://docs.micronaut.io/1.0.4/guide/index.html#quickStart)で出てくるような`@Controller`アノテーションもスコープアノテーションの一種のようなので、全部でこれだけではなさそうですけどね…。

また、DIが可能なコンテナ型（`Optional`とか`Iterable`とか）は、こちら。

[Injectable Container Types](https://docs.micronaut.io/1.0.4/guide/index.html#types)

Qualifiersもあるようです。

[Bean Qualifiers](https://docs.micronaut.io/1.0.4/guide/index.html#qualifiers)

ちょっと簡単に、触ってみましょう。


# 環境

今回の環境は、こちら。

```shell
$ mn -V
| Micronaut Version: 1.0.4
| JVM Version: 1.8.0_191
```

アプリケーションのひな型を作成。

```shell
$ mn create-app hello-di --build maven
$ cd hello-di
```

# サンプルアプリケーション

簡単な、`@Singleton`、`@Prototype`スコープを使ったサンプルを書いてみます。

`@Singleton`なService。

src/main/java/hello/di/service/SingletonService.java

```java
package hello.di.service;

import javax.inject.Singleton;

@Singleton
public class SingletonService {
    public String message() {
        return "Hello Singleton Bean";
    }
}
```

この`Service`を使用する、`Controller`。

src/main/java/hello/di/controller/SingletonBeanController.java

```java
package hello.di.controller;

import hello.di.service.SingletonService;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

@Controller("/singleton")
public class SingletonBeanController {
    SingletonService singletonService;

    public SingletonBeanController(SingletonService singletonService) {
        this.singletonService = singletonService;
    }

    @Get(produces = MediaType.TEXT_PLAIN)
    public String index() {
        return singletonService.message() + " from " + singletonService.getClass().getName() + "@" + singletonService.hashCode();
    }
}
```

コンストラクタインジェクションができるようです。

```java
    SingletonService singletonService;

    public SingletonBeanController(SingletonService singletonService) {
        this.singletonService = singletonService;
    }
```

また、インスタンスが同じことを確認するために、ハッシュコードを出してみたり。

```java
    @Get(produces = MediaType.TEXT_PLAIN)
    public String index() {
        return singletonService.message() + " from " + singletonService.getClass().getName() + "@" + singletonService.hashCode();
    }
```


続いて、`@Prototype`なService。

src/main/java/hello/di/service/PrototypeService.java

```java
package hello.di.service;

import io.micronaut.context.annotation.Prototype;

@Prototype
public class PrototypeService {
    public String message() {
        return "Hello Prototype Bean";
    }

}
```

この`Service`を使用する、`Controller`。

src/main/java/hello/di/controller/PrototypeBeanController.java

```java
package hello.di.controller;

import javax.inject.Inject;

import hello.di.service.PrototypeService;
import io.micronaut.context.ApplicationContext;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

@Controller("/prototype")
public class PrototypeBeanController {
    @Inject
    ApplicationContext applicationContext;

    @Get(produces = MediaType.TEXT_PLAIN)
    public String index() {
        PrototypeService prototypeService = applicationContext.getBean(PrototypeService.class);
        return prototypeService.message() + " from " + prototypeService.getClass().getName() + "@" + prototypeService.hashCode();
    }
}
```

`Controller`がSingletonらしく、そのままインジェクションすると、`@Prototype`なのに同じインスタンスになってしまったので、`ApplicatoinContext`から取得することに…。

```java
    @Get(produces = MediaType.TEXT_PLAIN)
    public String index() {
        PrototypeService prototypeService = applicationContext.getBean(PrototypeService.class);
        return prototypeService.message() + " from " + prototypeService.getClass().getName() + "@" + prototypeService.hashCode();
    }
```

クライアントプロキシがないからですかねぇ。

また、`@Inject`アノテーションが使えるようです。

```java
    @Inject
    ApplicationContext applicationContext;
```

`ApplicationContext`のコンストラクタインジェクションも、OKです。

```java
    PrototypeService prototypeService;

    public PrototypeBeanController(PrototypeService prototypeService) {
        this.prototypeService = prototypeService;
    }
```

mainメソッドを持ったクラスは、自動生成されたままなので省略します。


# 確認

`@Singleton`な`Service`を利用する、`Controller`にアクセス。

```shell
$ curl localhost:8080/singleton
Hello Singleton Bean from hello.di.service.SingletonService@56609473


$ curl localhost:8080/singleton
Hello Singleton Bean from hello.di.service.SingletonService@56609473


$ curl localhost:8080/singleton
Hello Singleton Bean from hello.di.service.SingletonService@56609473
```

DIが動作し、また同じ`Service`が返ってきていることが、確認できました。

続いて、`@Prototype`。

```shell
$ curl localhost:8080/prototype
Hello Prototype Bean from hello.di.service.PrototypeService@1219715735


$ curl localhost:8080/prototype
Hello Prototype Bean from hello.di.service.PrototypeService@1284304729


$ curl localhost:8080/prototype
Hello Prototype Bean from hello.di.service.PrototypeService@1807347832
```

こちらは、異なるインスタンスの`Service`が返ってきていることが確認できます。  
※最初、`@Prototype`な`Service`をなにも考えずにDIしていて、同じインスタンスが返ってきていたことに気づくという…


# 自動生成されたファイル

ところで、Annotation Processorでどんなファイルが生成されたのでしょう？

```shell
$ tree target
target
├── classes
│   ├── META-INF
│   │   ├── hello-di.kotlin_module
│   │   └── services
│   │       └── io.micronaut.inject.BeanDefinitionReference
│   ├── application.yml
│   ├── hello
│   │   └── di
│   │       ├── Application.class
│   │       ├── controller
│   │       │   ├── $PrototypeBeanControllerDefinition$$exec1$$AnnotationMetadata.class
│   │       │   ├── $PrototypeBeanControllerDefinition$$exec1.class
│   │       │   ├── $PrototypeBeanControllerDefinition.class
│   │       │   ├── $PrototypeBeanControllerDefinitionClass$$AnnotationMetadata.class
│   │       │   ├── $PrototypeBeanControllerDefinitionClass.class
│   │       │   ├── $SingletonBeanControllerDefinition$$exec1$$AnnotationMetadata.class
│   │       │   ├── $SingletonBeanControllerDefinition$$exec1.class
│   │       │   ├── $SingletonBeanControllerDefinition.class
│   │       │   ├── $SingletonBeanControllerDefinitionClass$$AnnotationMetadata.class
│   │       │   ├── $SingletonBeanControllerDefinitionClass.class
│   │       │   ├── PrototypeBeanController.class
│   │       │   └── SingletonBeanController.class
│   │       └── service
│   │           ├── $PrototypeServiceDefinition.class
│   │           ├── $PrototypeServiceDefinitionClass$$AnnotationMetadata.class
│   │           ├── $PrototypeServiceDefinitionClass.class
│   │           ├── $SingletonServiceDefinition.class
│   │           ├── $SingletonServiceDefinitionClass$$AnnotationMetadata.class
│   │           ├── $SingletonServiceDefinitionClass.class
│   │           ├── PrototypeService.class
│   │           └── SingletonService.class
│   └── logback.xml
└── generated-sources
    └── annotations

9 directories, 25 files
```

なんか、いろいろ作られてますね。

Service Provider向けのファイルができているようなので、こちらの中身も。

target/classes/META-INF/services/io.micronaut.inject.BeanDefinitionReference

``` 
hello.di.service.$PrototypeServiceDefinitionClass
hello.di.service.$SingletonServiceDefinitionClass
hello.di.controller.$SingletonBeanControllerDefinitionClass
hello.di.controller.$PrototypeBeanControllerDefinitionClass
```

とりあえず、雰囲気は確認できました。