Qualifying By Annotation Members
Since Micronaut 3.0, annotation qualifiers can also use annotation members to resolve the correct bean to inject. For example, consider the following annotation:
import io.micronaut.context.annotation.NonBinding;
import jakarta.inject.Qualifier;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier (1)
@Retention(RUNTIME)
public @interface Cylinders {
int value();
@NonBinding (2)
String description() default "";
}
import io.micronaut.context.annotation.NonBinding
import jakarta.inject.Qualifier
import java.lang.annotation.Retention
import static java.lang.annotation.RetentionPolicy.RUNTIME
@Qualifier (1)
@Retention(RUNTIME)
@interface Cylinders {
int value();
@NonBinding (2)
String description() default "";
}
import io.micronaut.context.annotation.NonBinding
import jakarta.inject.Qualifier
import kotlin.annotation.Retention
@Qualifier (1)
@Retention(AnnotationRetention.RUNTIME)
annotation class Cylinders(
val value: Int,
@get:NonBinding (2)
val description: String = ""
)
1 | The @Cylinders annotation is meta-annotated with @Qualifier |
2 | The annotation has two members. The @NonBinding annotation is used to exclude the description member from being considered during dependency resolution. |
You can then use the @Cylinders
annotation on any bean and the members that are not annotated with @NonBinding are considered during dependency resolution:
@Singleton
@Cylinders(value = 6, description = "6-cylinder V6 engine") (1)
public class V6Engine implements Engine { (2)
@Override
public int getCylinders() {
return 6;
}
@Override
public String start() {
return "Starting V6";
}
}
@Singleton
@Cylinders(value = 6, description = "6-cylinder V6 engine") (1)
class V6Engine implements Engine { (2)
@Override
int getCylinders() {
return 6
}
@Override
String start() {
return "Starting V6"
}
}
@Singleton
@Cylinders(value = 6, description = "6-cylinder V6 engine") (1)
class V6Engine : Engine { (2)
(2)
override val cylinders: Int
get() = 6
override fun start(): String {
return "Starting V6"
}
}
1 | Here the value member is set to 6 for the V6Engine type |
2 | The class implements an Engine interface |
@Singleton
@Cylinders(value = 8, description = "8-cylinder V8 engine") (1)
public class V8Engine implements Engine { (2)
@Override
public int getCylinders() {
return 8;
}
@Override
public String start() {
return "Starting V8";
}
}
@Singleton
@Cylinders(value = 8, description = "8-cylinder V8 engine") (1)
class V8Engine implements Engine { (2)
@Override
int getCylinders() {
return 8
}
@Override
String start() {
return "Starting V8"
}
}
@Singleton
@Cylinders(value = 8, description = "8-cylinder V8 engine") (1)
class V8Engine : Engine { (2)
override val cylinders: Int
get() = 8
override fun start(): String {
return "Starting V8"
}
}
1 | Here the value member is set to 8 for the V8Engine type |
2 | The class implements an Engine interface |
You can then use the @Cylinders
qualifier on any injection point to select the correct bean to inject. For example:
@Inject Vehicle(@Cylinders(8) Engine engine) {
this.engine = engine;
}
@Inject Vehicle(@Cylinders(8) Engine engine) {
this.engine = engine
}
@Singleton
class Vehicle(@param:Cylinders(8) val engine: Engine) {
fun start(): String {
return engine.start()
}
}