如果要注入的给定接口有多个可能的实现,则需要使用限定符。
Micronaut 再次利用 JSR-330 以及限定符和命名注释来支持此用例。
按名称限定
要按名称限定,请使用 Named 注释。例如,考虑以下类:
Java |
Groovy |
Kotlin |
public interface Engine { // (1)
int getCylinders();
String start();
}
@Singleton
public class V6Engine implements Engine { // (2)
@Override
public String start() {
return "Starting V6";
}
@Override
public int getCylinders() {
return 6;
}
}
@Singleton
public class V8Engine implements Engine { // (3)
@Override
public String start() {
return "Starting V8";
}
@Override
public int getCylinders() {
return 8;
}
}
@Singleton
public class Vehicle {
private final Engine engine;
@Inject
public Vehicle(@Named("v8") Engine engine) {// (4)
this.engine = engine;
}
public String start() {
return engine.start();// (5)
}
}
|
interface Engine { // (1)
int getCylinders()
String start()
}
@Singleton
class V6Engine implements Engine { // (2)
int cylinders = 6
@Override
String start() {
"Starting V6"
}
}
@Singleton
class V8Engine implements Engine { // (3)
int cylinders = 8
@Override
String start() {
"Starting V8"
}
}
@Singleton
class Vehicle {
final Engine engine
@Inject Vehicle(@Named('v8') Engine engine) { // (4)
this.engine = engine
}
String start() {
engine.start() // (5)
}
}
|
interface Engine { // (1)
val cylinders: Int
fun start(): String
}
@Singleton
class V6Engine : Engine { // (2)
override var cylinders: Int = 6
override fun start(): String {
return "Starting V6"
}
}
@Singleton
class V8Engine : Engine {
override var cylinders: Int = 8
override fun start(): String {
return "Starting V8"
}
}
@Singleton
class Vehicle @Inject
constructor(@param:Named("v8") private val engine: Engine) { // (4)
fun start(): String {
return engine.start() // (5)
}
}
|
-
Engine 接口定义了公共契约
-
V6Engine 类是第一个实现
-
V8Engine 类是第二个实现
-
javax.inject.Named注解表示需要V8Engine实现
-
调用 start 方法打印:“Starting V8”
Micronaut 能够在前面的示例中注入 V8Engine,因为:
@Named 限定符值 (v8) + 被注入的类型简单名称 (Engine) ==(不区分大小写)== Engine (V8Engine) 类型的 bean 的简单名称
您还可以在 bean 的类级别声明 @Named 以显式定义 bean 的名称。
通过注释限定
除了能够按名称进行限定外,您还可以使用 Qualifier 注释构建自己的限定符。例如,考虑以下注解:
Java |
Groovy |
Kotlin |
import jakarta.inject.Qualifier;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Retention(RUNTIME)
public @interface V8 {
}
|
import jakarta.inject.Qualifier
import java.lang.annotation.Retention
import static java.lang.annotation.RetentionPolicy.RUNTIME
@Qualifier
@Retention(RUNTIME)
@interface V8 {
}
|
import jakarta.inject.Qualifier
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy.RUNTIME
@Qualifier
@Retention(RUNTIME)
annotation class V8
|
上面的注释本身使用@Qualifier 注释进行注释,以将其指定为限定符。然后,您可以在代码中的任何注入点使用注释。例如:
Java |
Groovy |
Kotlin |
@Inject Vehicle(@V8 Engine engine) {
this.engine = engine;
}
|
@Inject Vehicle(@V8 Engine engine) {
this.engine = engine
}
|
@Inject constructor(@V8 val engine: Engine) {
|
注释成员限定
从 Micronaut 3.0 开始,注释限定符还可以使用注释成员来解析要注入的正确 bean。例如,考虑以下注解:
Java |
Groovy |
Kotlin |
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 = ""
)
|
@Cylinders 注释使用@Qualifier 进行元注释
注释有两个成员。 @NonBinding 注释用于在依赖项解析期间将 description 成员排除在考虑范围之外。
然后,您可以在任何 bean 上使用 @Cylinders 注释,并且在依赖项解析期间会考虑未使用 @NonBinding 注释的成员:
Java |
Groovy |
Kotlin |
@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"
}
}
|
这里的值成员为 V6Engine 类型设置为 6
该类实现了一个 Engine 接口
Java |
Groovy |
Kotlin |
@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"
}
}
|
这里的值成员设置为 8 对于 V8Engine 类型
该类实现了一个 Engine 接口
然后,您可以在任何注入点上使用 @Cylinders 限定符来选择要注入的正确 bean。例如:
Java |
Groovy |
Kotlin |
@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()
}
}
|
通过泛型类型参数进行限定
从 Micronaut 3.0 开始,可以根据类或接口的泛型类型参数来选择注入哪个 bean。考虑以下示例:
Java |
Groovy |
Kotlin |
public interface CylinderProvider {
int getCylinders();
}
|
interface CylinderProvider {
int getCylinders()
}
|
interface CylinderProvider {
val cylinders: Int
}
|
CylinderProvider接口提供了 cylinders 的数量。
Java |
Groovy |
Kotlin |
public interface Engine<T extends CylinderProvider> { // (1)
default int getCylinders() {
return getCylinderProvider().getCylinders();
}
default String start() {
return "Starting " + getCylinderProvider().getClass().getSimpleName();
}
T getCylinderProvider();
}
|
interface Engine<T extends CylinderProvider> { // (1)
default int getCylinders() { cylinderProvider.cylinders }
default String start() { "Starting ${cylinderProvider.class.simpleName}" }
T getCylinderProvider()
}
|
interface Engine<T : CylinderProvider> { // (1)
val cylinders: Int
get() = cylinderProvider.cylinders
fun start(): String {
return "Starting ${cylinderProvider.javaClass.simpleName}"
}
val cylinderProvider: T
}
|
引擎类定义了一个泛型类型参数 <T>,它必须是 CylinderProvider 的一个实例
您可以使用不同的通用类型参数定义 Engine 接口的实现。例如对于 V6 引擎:
Java |
Groovy |
Kotlin |
public class V6 implements CylinderProvider {
@Override
public int getCylinders() {
return 6;
}
}
|
class V6 implements CylinderProvider {
@Override
int getCylinders() { 6 }
}
|
class V6 : CylinderProvider {
override val cylinders: Int = 6
}
|
上面定义了一个实现了 CylinderProvider 接口的 V6 类。
Java |
Groovy |
Kotlin |
@Singleton
public class V6Engine implements Engine<V6> { // (1)
@Override
public V6 getCylinderProvider() {
return new V6();
}
}
|
@Singleton
class V6Engine implements Engine<V6> { // (1)
@Override
V6 getCylinderProvider() { new V6() }
}
|
@Singleton
class V6Engine : Engine<V6> { // (1)
override val cylinderProvider: V6
get() = V6()
}
|
- V6Engine 实现 Engine 提供 V6 作为通用类型参数
和 V8 引擎:
Java |
Groovy |
Kotlin |
public class V8 implements CylinderProvider {
@Override
public int getCylinders() {
return 8;
}
}
|
class V8 implements CylinderProvider {
@Override
int getCylinders() { 8 }
}
|
class V8 : CylinderProvider {
override val cylinders: Int = 8
}
|
上面定义了一个实现了 CylinderProvider 接口的 V8 类。
Java |
Groovy |
Kotlin |
@Singleton
public class V8Engine implements Engine<V8> { // (1)
@Override
public V8 getCylinderProvider() {
return new V8();
}
}
|
@Singleton
class V8Engine implements Engine<V8> { // (1)
@Override
V8 getCylinderProvider() { new V8() }
}
|
@Singleton
class V8Engine : Engine<V8> { // (1)
override val cylinderProvider: V8
get() = V8()
}
|
V8Engine 实现 Engine 提供 V8 作为通用类型参数
然后,您可以在定义注入点时使用泛型参数,Micronaut 将根据特定的泛型类型参数选择正确的 bean 进行注入:
Java |
Groovy |
Kotlin |
@Inject
public Vehicle(Engine<V8> engine) {
this.engine = engine;
}
|
@Inject
Vehicle(Engine<V8> engine) {
this.engine = engine
}
|
@Singleton
class Vehicle(val engine: Engine<V8>) {
|
在上面的示例中,注入了 V8Engine bean。
初级和次级 Bean
Primary 是一个限定符,表示在多个接口实现的情况下,一个 bean 是要选择的主要 bean。
考虑以下示例:
Java |
Groovy |
Kotlin |
public interface ColorPicker {
String color();
}
|
interface ColorPicker {
String color()
}
|
interface ColorPicker {
fun color(): String
}
|
ColorPicker 由这些类实现:
初级 Bean
Java |
Groovy |
Kotlin |
import io.micronaut.context.annotation.Primary;
import jakarta.inject.Singleton;
@Primary
@Singleton
class Green implements ColorPicker {
@Override
public String color() {
return "green";
}
}
|
import io.micronaut.context.annotation.Primary
import jakarta.inject.Singleton
@Primary
@Singleton
class Green implements ColorPicker {
@Override
String color() {
return "green"
}
}
|
import io.micronaut.context.annotation.Primary
import jakarta.inject.Singleton
@Primary
@Singleton
class Green: ColorPicker {
override fun color(): String {
return "green"
}
}
|
Green bean 类实现了 ColorPicker 并使用@Primary 注释。
另一个相同类型的 Bean
Java |
Groovy |
Kotlin |
import jakarta.inject.Singleton;
@Singleton
public class Blue implements ColorPicker {
@Override
public String color() {
return "blue";
}
}
|
import jakarta.inject.Singleton
@Singleton
class Blue implements ColorPicker {
@Override
String color() {
return "blue"
}
}
|
import jakarta.inject.Singleton
@Singleton
class Blue: ColorPicker {
override fun color(): String {
return "blue"
}
}
|
Blue bean 类还实现了 ColorPicker,因此在注入 ColorPicker 接口时您有两个可能的候选对象。由于绿色是主要的,所以它总是受到青睐。
Java |
Groovy |
Kotlin |
@Controller("/testPrimary")
public class TestController {
protected final ColorPicker colorPicker;
public TestController(ColorPicker colorPicker) { // (1)
this.colorPicker = colorPicker;
}
@Get
public String index() {
return colorPicker.color();
}
}
|
@Controller("/test")
class TestController {
protected final ColorPicker colorPicker
TestController(ColorPicker colorPicker) { // (1)
this.colorPicker = colorPicker
}
@Get
String index() {
colorPicker.color()
}
}
|
@Controller("/test")
class TestController(val colorPicker: ColorPicker) { // (1)
@Get
fun index(): String {
return colorPicker.color()
}
}
|
尽管有两个 ColorPicker bean,但由于 @Primary 注释,Green 被注入。
如果存在多个可能的候选者并且没有定义 @Primary,则抛出 NonUniqueBeanException。
除了 @Primary 之外,还有一个 Secondary 注释会导致相反的效果并允许降低 bean 的优先级。
注入任何 Bean
如果您不特别注意注入哪个 bean,那么您可以使用 @Any 限定符来注入第一个可用的 bean,例如:
注入任何实例
Java |
Groovy |
Kotlin |
@Inject @Any
Engine engine;
|
@Inject @Any
Engine engine
|
@Inject
@field:Any
lateinit var engine: Engine
|
@Any 限定符通常与 BeanProvider 接口结合使用以允许更多动态用例。例如,如果存在 bean,则以下 Vehicle 实现将启动引擎:
将 BeanProvider 与 Any 一起使用
Java |
Groovy |
Kotlin |
import io.micronaut.context.BeanProvider;
import io.micronaut.context.annotation.Any;
import jakarta.inject.Singleton;
@Singleton
public class Vehicle {
final BeanProvider<Engine> engineProvider;
public Vehicle(@Any BeanProvider<Engine> engineProvider) { // (1)
this.engineProvider = engineProvider;
}
void start() {
engineProvider.ifPresent(Engine::start); // (2)
}
}
|
import io.micronaut.context.BeanProvider
import io.micronaut.context.annotation.Any
import jakarta.inject.Singleton
@Singleton
class Vehicle {
final BeanProvider<Engine> engineProvider
Vehicle(@Any BeanProvider<Engine> engineProvider) { // (1)
this.engineProvider = engineProvider
}
void start() {
engineProvider.ifPresent(Engine::start) // (2)
}
}
|
import io.micronaut.context.BeanProvider
import io.micronaut.context.annotation.Any
import jakarta.inject.Singleton
@Singleton
class Vehicle(@param:Any val engineProvider: BeanProvider<Engine>) { // (1)
fun start() {
engineProvider.ifPresent { it.start() } // (2)
}
fun startAll() {
if (engineProvider.isPresent) { // (1)
engineProvider.forEach { it.start() } // (2)
}
}
|
使用@Any 注入 BeanProvider
如果使用 ifPresent 方法存在底层 bean,则调用 start 方法
如果有多个 bean,您还可以调整行为。以下示例启动 Vehicle 中安装的所有引擎(如果存在):
将 BeanProvider 与 Any 一起使用
Java |
Groovy |
Kotlin |
void startAll() {
if (engineProvider.isPresent()) { // (1)
engineProvider.stream().forEach(Engine::start); // (2)
}
}
|
void startAll() {
if (engineProvider.isPresent()) { // (1)
engineProvider.each {it.start() } // (2)
}
}
|
fun startAll() {
if (engineProvider.isPresent) { // (1)
engineProvider.forEach { it.start() } // (2)
}
|
检查是否有 bean 存在
如果是这样,通过 stream().forEach(..) 方法遍历每一个,启动引擎
更多建议: