Micronaut 的依賴注入系統(tǒng)和 Spring 的一個顯著區(qū)別是替換 bean 的方式。
在 Spring 應用程序中,bean 具有名稱,并且通過創(chuàng)建具有相同名稱的 bean 來覆蓋,而不管 bean 的類型如何。 Spring 也有 bean 注冊順序的概念,因此在 Spring Boot 中你有 @AutoConfigureBefore 和 @AutoConfigureAfter 注釋來控制 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]
}
}
|
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。
更多建議: