如果要注入的給定接口有多個(gè)可能的實(shí)現(xiàn),則需要使用限定符。
Micronaut 再次利用 JSR-330 以及限定符和命名注釋來支持此用例。
按名稱限定
要按名稱限定,請(qǐng)使用 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 類是第一個(gè)實(shí)現(xiàn)
-
V8Engine 類是第二個(gè)實(shí)現(xiàn)
-
javax.inject.Named注解表示需要V8Engine實(shí)現(xiàn)
-
調(diào)用 start 方法打?。骸癝tarting V8”
Micronaut 能夠在前面的示例中注入 V8Engine,因?yàn)椋?/p>
@Named 限定符值 (v8) + 被注入的類型簡(jiǎn)單名稱 (Engine) ==(不區(qū)分大小寫)== Engine (V8Engine) 類型的 bean 的簡(jiǎn)單名稱
您還可以在 bean 的類級(jí)別聲明 @Named 以顯式定義 bean 的名稱。
通過注釋限定
除了能夠按名稱進(jìn)行限定外,您還可以使用 Qualifier 注釋構(gòu)建自己的限定符。例如,考慮以下注解:
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 注釋進(jìn)行注釋,以將其指定為限定符。然后,您可以在代碼中的任何注入點(diǎn)使用注釋。例如:
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 進(jìn)行元注釋
注釋有兩個(gè)成員。 @NonBinding 注釋用于在依賴項(xiàng)解析期間將 description 成員排除在考慮范圍之外。
然后,您可以在任何 bean 上使用 @Cylinders 注釋,并且在依賴項(xiàng)解析期間會(huì)考慮未使用 @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 類型設(shè)置為 6
該類實(shí)現(xiàn)了一個(gè) 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"
}
}
|
這里的值成員設(shè)置為 8 對(duì)于 V8Engine 類型
該類實(shí)現(xiàn)了一個(gè) Engine 接口
然后,您可以在任何注入點(diǎn)上使用 @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()
}
}
|
通過泛型類型參數(shù)進(jìn)行限定
從 Micronaut 3.0 開始,可以根據(jù)類或接口的泛型類型參數(shù)來選擇注入哪個(gè) bean??紤]以下示例:
Java |
Groovy |
Kotlin |
public interface CylinderProvider {
int getCylinders();
}
|
interface CylinderProvider {
int getCylinders()
}
|
interface CylinderProvider {
val cylinders: Int
}
|
CylinderProvider接口提供了 cylinders 的數(shù)量。
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
}
|
引擎類定義了一個(gè)泛型類型參數(shù) <T>,它必須是 CylinderProvider 的一個(gè)實(shí)例
您可以使用不同的通用類型參數(shù)定義 Engine 接口的實(shí)現(xiàn)。例如對(duì)于 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
}
|
上面定義了一個(gè)實(shí)現(xiàn)了 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 實(shí)現(xiàn) Engine 提供 V6 作為通用類型參數(shù)
和 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
}
|
上面定義了一個(gè)實(shí)現(xiàn)了 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 實(shí)現(xiàn) Engine 提供 V8 作為通用類型參數(shù)
然后,您可以在定義注入點(diǎn)時(shí)使用泛型參數(shù),Micronaut 將根據(jù)特定的泛型類型參數(shù)選擇正確的 bean 進(jìn)行注入:
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。
初級(jí)和次級(jí) Bean
Primary 是一個(gè)限定符,表示在多個(gè)接口實(shí)現(xiàn)的情況下,一個(gè) bean 是要選擇的主要 bean。
考慮以下示例:
Java |
Groovy |
Kotlin |
public interface ColorPicker {
String color();
}
|
interface ColorPicker {
String color()
}
|
interface ColorPicker {
fun color(): String
}
|
ColorPicker 由這些類實(shí)現(xiàn):
初級(jí) 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 類實(shí)現(xiàn)了 ColorPicker 并使用@Primary 注釋。
另一個(gè)相同類型的 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 類還實(shí)現(xiàn)了 ColorPicker,因此在注入 ColorPicker 接口時(shí)您有兩個(gè)可能的候選對(duì)象。由于綠色是主要的,所以它總是受到青睞。
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()
}
}
|
盡管有兩個(gè) ColorPicker bean,但由于 @Primary 注釋,Green 被注入。
如果存在多個(gè)可能的候選者并且沒有定義 @Primary,則拋出 NonUniqueBeanException。
除了 @Primary 之外,還有一個(gè) Secondary 注釋會(huì)導(dǎo)致相反的效果并允許降低 bean 的優(yōu)先級(jí)。
注入任何 Bean
如果您不特別注意注入哪個(gè) bean,那么您可以使用 @Any 限定符來注入第一個(gè)可用的 bean,例如:
注入任何實(shí)例
Java |
Groovy |
Kotlin |
@Inject @Any
Engine engine;
|
@Inject @Any
Engine engine
|
@Inject
@field:Any
lateinit var engine: Engine
|
@Any 限定符通常與 BeanProvider 接口結(jié)合使用以允許更多動(dòng)態(tài)用例。例如,如果存在 bean,則以下 Vehicle 實(shí)現(xiàn)將啟動(dòng)引擎:
將 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,則調(diào)用 start 方法
如果有多個(gè) bean,您還可以調(diào)整行為。以下示例啟動(dòng) 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(..) 方法遍歷每一個(gè),啟動(dòng)引擎
更多建議: