Micronaut Bean 替換

2023-02-27 16:21 更新

Micronaut 的依賴注入系統(tǒng)和 Spring 的一個顯著區(qū)別是替換 bean 的方式。

在 Spring 應用程序中,bean 具有名稱,并且通過創(chuàng)建具有相同名稱的 bean 來覆蓋,而不管 bean 的類型如何。 Spring 也有 bean 注冊順序的概念,因此在 Spring Boot 中你有 @AutoConfigureBefore 和 @AutoConfigureAfter 注釋來控制 bean 如何相互覆蓋。

這種策略會導致難以調試的問題,例如:

  • Bean加載順序改變,導致意外結果

  • 具有相同名稱的 bean 覆蓋具有不同類型的另一個 bean

為了避免這些問題,Micronaut 的 DI 沒有 bean 名稱或加載順序的概念。 Beans 有一個類型和一個限定符。您不能用另一個覆蓋完全不同類型的 bean。

Spring 方法的一個有用好處是它允許覆蓋現有 bean 以自定義行為。為了支持同樣的能力,Micronaut 的 DI 提供了一個顯式的 @Replaces 注解,它很好地集成了對條件 Bean 的支持,并清楚地記錄和表達了開發(fā)人員的意圖。

任何現有的 bean 都可以被另一個聲明了 @Replaces 的 bean 替換。例如,考慮以下類:

JdbcBookService

 Java Groovy  Kotlin 
@Singleton
@Requires(beans = DataSource.class)
@Requires(property = "datasource.url")
public class JdbcBookService implements BookService {

    DataSource dataSource;

    public JdbcBookService(DataSource dataSource) {
        this.dataSource = dataSource;
    }
@Singleton
@Requires(beans = DataSource)
@Requires(property = "datasource.url")
class JdbcBookService implements BookService {

    DataSource dataSource
@Singleton
@Requirements(Requires(beans = [DataSource::class]), Requires(property = "datasource.url"))
class JdbcBookService(internal var dataSource: DataSource) : BookService {

你可以在 src/test/java 中定義一個類來替換這個類只是為了你的測試:

Using @Replaces

 Java Groovy  Kotlin 
@Replaces(JdbcBookService.class) // (1)
@Singleton
public class MockBookService implements BookService {

    Map<String, Book> bookMap = new LinkedHashMap<>();

    @Override
    public Book findBook(String title) {
        return bookMap.get(title);
    }
}
@Replaces(JdbcBookService.class) // (1)
@Singleton
class MockBookService implements BookService {

    Map<String, Book> bookMap = [:]

    @Override
    Book findBook(String title) {
        bookMap.get(title)
    }
}
@Replaces(JdbcBookService::class) // (1)
@Singleton
class MockBookService : BookService {

    var bookMap: Map<String, Book> = LinkedHashMap()

    override fun findBook(title: String): Book? {
        return bookMap[title]
    }
}
  1. MockBookService 聲明它替換 JdbcBookService

替換 Factory

@Replaces 注釋還支持工廠參數。該參數允許替換整個工廠 bean 或工廠創(chuàng)建的特定類型。

例如,可能需要替換給定工廠類的全部或部分:

BookFactory

 Java Groovy  Kotlin 
@Factory
public class BookFactory {

    @Singleton
    Book novel() {
        return new Book("A Great Novel");
    }

    @Singleton
    TextBook textBook() {
        return new TextBook("Learning 101");
    }
}
@Factory
class BookFactory {

    @Singleton
    Book novel() {
        new Book('A Great Novel')
    }

    @Singleton
    TextBook textBook() {
        new TextBook('Learning 101')
    }
}
@Factory
class BookFactory {

    @Singleton
    internal fun novel(): Book {
        return Book("A Great Novel")
    }

    @Singleton
    internal fun textBook(): TextBook {
        return TextBook("Learning 101")
    }
}

要完全替換工廠,您的工廠方法必須與被替換工廠中所有方法的返回類型相匹配。

在此示例中,BookFactory#textBook() 未被替換,因為該工廠沒有返回 TextBook 的工廠方法。

CustomBookFactory

 Java Groovy  Kotlin 
@Factory
@Replaces(factory = BookFactory.class)
public class CustomBookFactory {

    @Singleton
    Book otherNovel() {
        return new Book("An OK Novel");
    }
}
@Factory
@Replaces(factory = BookFactory)
class CustomBookFactory {

    @Singleton
    Book otherNovel() {
        new Book('An OK Novel')
    }
}
@Factory
@Replaces(factory = BookFactory::class)
class CustomBookFactory {

    @Singleton
    internal fun otherNovel(): Book {
        return Book("An OK Novel")
    }
}

要替換一個或多個工廠方法但保留其余方法,請在方法上應用 @Replaces 注釋并表示要應用的工廠。

TextBookFactory

 Java Groovy  Kotlin 
@Factory
public class TextBookFactory {

    @Singleton
    @Replaces(value = TextBook.class, factory = BookFactory.class)
    TextBook textBook() {
        return new TextBook("Learning 305");
    }
}
@Factory
class TextBookFactory {

    @Singleton
    @Replaces(value = TextBook, factory = BookFactory)
    TextBook textBook() {
        new TextBook('Learning 305')
    }
}
@Factory
class TextBookFactory {

    @Singleton
    @Replaces(value = TextBook::class, factory = BookFactory::class)
    internal fun textBook(): TextBook {
        return TextBook("Learning 305")
    }
}

BookFactory#novel() 方法不會被替換,因為 TextBook 類是在注解中定義的。

默認實現

公開 API 時,可能不希望將接口的默認實現公開為公共 API 的一部分。這樣做會阻止用戶替換實現,因為他們將無法引用該類。解決方案是用 DefaultImplementation 注釋接口,以指示如果用戶創(chuàng)建一個 @Replaces(YourInterface.class) 的 bean,要替換哪個實現。

例如考慮:

A public API contract

 Java Groovy  Kotlin 
import io.micronaut.context.annotation.DefaultImplementation;

@DefaultImplementation(DefaultResponseStrategy.class)
public interface ResponseStrategy {
}
import io.micronaut.context.annotation.DefaultImplementation

@DefaultImplementation(DefaultResponseStrategy)
interface ResponseStrategy {
}
import io.micronaut.context.annotation.DefaultImplementation

@DefaultImplementation(DefaultResponseStrategy::class)
interface ResponseStrategy

默認實現

 Java Groovy  Kotlin 
import jakarta.inject.Singleton;

@Singleton
class DefaultResponseStrategy implements ResponseStrategy {

}
import jakarta.inject.Singleton

@Singleton
class DefaultResponseStrategy implements ResponseStrategy {

}
import jakarta.inject.Singleton

@Singleton
internal class DefaultResponseStrategy : ResponseStrategy

自定義實現

 Java Groovy  Kotlin 
import io.micronaut.context.annotation.Replaces;
import jakarta.inject.Singleton;

@Singleton
@Replaces(ResponseStrategy.class)
public class CustomResponseStrategy implements ResponseStrategy {

}
import io.micronaut.context.annotation.Replaces
import jakarta.inject.Singleton

@Singleton
@Replaces(ResponseStrategy)
class CustomResponseStrategy implements ResponseStrategy {

}
import io.micronaut.context.annotation.Replaces
import jakarta.inject.Singleton

@Singleton
@Replaces(ResponseStrategy::class)
class CustomResponseStrategy : ResponseStrategy

在上面的示例中,CustomResponseStrategy 替換了 DefaultResponseStrategy,因為 DefaultImplementation 注釋指向 DefaultResponseStrategy。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號