Micronaut Bean 限定符

2023-02-27 14:39 更新

如果要注入的給定接口有多個(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)
    }
}
  1. Engine 接口定義了公共契約

  2. V6Engine 類是第一個(gè)實(shí)現(xiàn)

  3. V8Engine 類是第二個(gè)實(shí)現(xiàn)

  4. javax.inject.Named注解表示需要V8Engine實(shí)現(xiàn)

  5. 調(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 = ""
)
  1. @Cylinders 注釋使用@Qualifier 進(jìn)行元注釋

  2. 注釋有兩個(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"
    }
}
  1. 這里的值成員為 V6Engine 類型設(shè)置為 6

  2. 該類實(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"
    }
}
  1. 這里的值成員設(shè)置為 8 對(duì)于 V8Engine 類型

  2. 該類實(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
}
  1. 引擎類定義了一個(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()
}
  1.  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()
}
  1. 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()
    }
}
  1. 盡管有兩個(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)
        }
}
  1. 使用@Any 注入 BeanProvider

  2. 如果使用 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)
    }
  1. 檢查是否有 bean 存在

  2. 如果是這樣,通過 stream().forEach(..) 方法遍歷每一個(gè),啟動(dòng)引擎


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)