LoginSignup
1
7

More than 5 years have passed since last update.

MicronautでDIしてみる

Last updated at Posted at 2019-02-08

MicronautでDI

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

Inversion of Control

特徴としては、

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

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

MicronautのDIで使うスコープ

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

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

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

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

とはいえ、Quick Startで出てくるような@Controllerアノテーションもスコープアノテーションの一種のようなので、全部でこれだけではなさそうですけどね…。

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

Injectable Container Types

Qualifiersもあるようです。

Bean Qualifiers

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

環境

今回の環境は、こちら。

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

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

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

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

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

@SingletonなService。

src/main/java/hello/di/service/SingletonService.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

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();
    }
}

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

    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();
    }

続いて、@PrototypeなService。

src/main/java/hello/di/service/PrototypeService.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

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から取得することに…。

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

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

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

    @Inject
    ApplicationContext applicationContext;

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

    PrototypeService prototypeService;

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

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

確認

@SingletonServiceを利用する、Controllerにアクセス。

$ 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

$ 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が返ってきていることが確認できます。

※最初、@PrototypeServiceをなにも考えずにDIしていて、同じインスタンスが返ってきていたことに気づくという…

自動生成されたファイル

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

$ 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

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

1
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
7