最近、なんとなくMicronautを触り始めてる。
## 気になった
順番にドキュメントを読んでいってて、この部分が気になった。
https://docs.micronaut.io/latest/guide/index.html#qualifiers
import javax.inject.* interface Engine { int getCylinders() String start() } @Singleton class V6Engine implements Engine { int cylinders = 6 String start() { "Starting V6" } } @Singleton class V8Engine implements Engine { int cylinders = 8 String start() { "Starting V8" } } @Singleton class Vehicle { final Engine engine @Inject Vehicle(@Named('v8') Engine engine) { this.engine = engine } String start() { engine.start() } }
Engineの実装として、V6EngineとV8Engineが定義されていて、@Namedアノテーションで名前を指定してInjectできるよ、という部分。
この「名前」の部分、どういう動きでv8だけで取得できるんだろう?というのが気になった。v8engineだったらクラス名かなーと思うんだけど。
## とりあえず動かす
サンプルコードはGroovyかな?なので、セミコロンを後ろにつけたりして、手元にあったHelloControllerにコンストラクターインジェクションで差し込んでみた。
package hello.world; import io.micronaut.http.MediaType; import io.micronaut.http.annotation.*; import javax.inject.*; @Controller("/hello") public class HelloController { final Engine engine; HelloController(@Named("v8") Engine engine){ this.engine = engine; } @Get(produces = MediaType.TEXT_PLAIN) public String index() { return "Hello World. " + engine.start(); } } interface Engine { int getCylinders(); String start(); } @Singleton class V6Engine implements Engine { int cylinders = 6; @Override public int getCylinders() { return cylinders; } @Override public String start() { return "Starting V6"; } } @Singleton class V8Engine implements Engine { int cylinders = 8; @Override public int getCylinders() { return cylinders; } @Override public String start() { return "Starting V8"; } }
- 確かに
v8だけで動く v6を指定するとV6Engineになるv8engineでも動く- 前方一致なのかな?と思って
v8engにしてみたら動かない
ほほー。
## 実装クラスに名前を指定
@Namedアノテーションをつけると実装クラスに名前をつけることができる。
@Singleton @Named("sss") class V8Engine implements Engine { int cylinders = 8;
sssでInjectできるv8ではInjectできなくなったv8engineでもInjectできなくなった
なるほどー。
## ということで実装を探した
ここだな。
String typeName; AnnotationMetadata annotationMetadata = candidate.getAnnotationMetadata(); // here we resolved the declared Qualifier of the bean Optional<String> beanQualifier = annotationMetadata.findDeclaredAnnotation(Named.class).flatMap(namedAnnotationValue -> namedAnnotationValue.getValue(String.class)); typeName = beanQualifier.orElseGet(() -> { if (candidate instanceof NameResolver) { Optional<String> resolvedName = ((NameResolver) candidate).resolveName(); return resolvedName.orElse(candidate.getBeanType().getSimpleName()); } return candidate.getBeanType().getSimpleName(); }); return typeName.equalsIgnoreCase(name) || typeName.equalsIgnoreCase(name + beanType.getSimpleName()); }
- 実装クラスに
@Namedがついてる場合は- 1-1. 指定した名前が、
@Namedのvalueと一致すればOK
- 1-1. 指定した名前が、
- 実装クラスに
@Namedがついていない場合は- 2-1. 指定した名前が、大文字小文字を無視して実装クラスのSimpleNameと一致すればOK
- 2-2. 指定した名前 + BeanTypeのSimpleNameが、大文字小文字を無視して実装クラスのSimpleNameと一致すればOK
そっかー。だからv8だけを指定した場合は、2-2で、v8にEngineインターフェース名がくっついたものと一致してるってことか。
すっきり。