阅读(3305) (0)

Micronaut 自定义类型转换器

2023-02-23 11:02:52 更新

Micronaut 包含一个可扩展的类型转换机制。要添加额外的类型转换器,您需要注册 TypeConverter 类型的 bean。

以下示例显示如何使用内置转换器之一(映射到对象)或创建您自己的转换器。

考虑以下 ConfigurationProperties:

 Java Groovy  Kotlin 
@ConfigurationProperties(MyConfigurationProperties.PREFIX)
public class MyConfigurationProperties {

    public static final String PREFIX = "myapp";

    protected LocalDate updatedAt;

    public LocalDate getUpdatedAt() {
        return updatedAt;
    }
}
@ConfigurationProperties(MyConfigurationProperties.PREFIX)
class MyConfigurationProperties {

    public static final String PREFIX = "myapp"

    protected LocalDate updatedAt

    LocalDate getUpdatedAt() {
        updatedAt
    }
}
@ConfigurationProperties(MyConfigurationProperties.PREFIX)
class MyConfigurationProperties {

    var updatedAt: LocalDate? = null
        protected set

    companion object {
        const val PREFIX = "myapp"
    }
}

MyConfigurationProperties 类型有一个名为 updatedAt 的 LocalDate 类型的属性。

要通过配置从地图绑定此属性:

 Java Groovy   Kotlin
private static ApplicationContext ctx;

@BeforeClass
public static void setupCtx() {
    ctx = ApplicationContext.run(
            new LinkedHashMap<String, Object>() {{
                put("myapp.updatedAt", // (1)
                        new LinkedHashMap<String, Integer>() {{
                            put("day", 28);
                            put("month", 10);
                            put("year", 1982);
                        }}
                );
            }}
    );
}

@AfterClass
public static void teardownCtx() {
    if(ctx != null) {
        ctx.stop();
    }
}
@AutoCleanup
@Shared
ApplicationContext ctx = ApplicationContext.run(
        "myapp.updatedAt": [day: 28, month: 10, year: 1982]  // (1)
)
lateinit var ctx: ApplicationContext

@BeforeEach
fun setup() {
    ctx = ApplicationContext.run(
        mapOf(
            "myapp.updatedAt" to mapOf( // (1)
                "day" to 28,
                "month" to 10,
                "year" to 1982
            )
        )
    )
}

@AfterEach
fun teardown() {
    ctx?.close()
}
  1. 请注意我们如何在上面的 MyConfigurationProperties 类中匹配 myapp 前缀和 updatedAt 属性名称

这在默认情况下不起作用,因为没有从 Map 到 LocalDate 的内置转换。要解决此问题,请定义自定义 TypeConverter:

 Java Groovy  Kotlin 
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.TypeConverter;

import jakarta.inject.Singleton;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.util.Map;
import java.util.Optional;

@Singleton
public class MapToLocalDateConverter implements TypeConverter<Map, LocalDate> { // (1)
    @Override
    public Optional<LocalDate> convert(Map propertyMap, Class<LocalDate> targetType, ConversionContext context) {
        Optional<Integer> day = ConversionService.SHARED.convert(propertyMap.get("day"), Integer.class);
        Optional<Integer> month = ConversionService.SHARED.convert(propertyMap.get("month"), Integer.class);
        Optional<Integer> year = ConversionService.SHARED.convert(propertyMap.get("year"), Integer.class);
        if (day.isPresent() && month.isPresent() && year.isPresent()) {
            try {
                return Optional.of(LocalDate.of(year.get(), month.get(), day.get())); // (2)
            } catch (DateTimeException e) {
                context.reject(propertyMap, e); // (3)
                return Optional.empty();
            }
        }

        return Optional.empty();
    }
}
import io.micronaut.core.convert.ConversionContext
import io.micronaut.core.convert.ConversionService
import io.micronaut.core.convert.TypeConverter

import jakarta.inject.Singleton
import java.time.DateTimeException
import java.time.LocalDate

@Singleton
class MapToLocalDateConverter implements TypeConverter<Map, LocalDate> { // (1)
    @Override
    Optional<LocalDate> convert(Map propertyMap, Class<LocalDate> targetType, ConversionContext context) {
        Optional<Integer> day = ConversionService.SHARED.convert(propertyMap.day, Integer)
        Optional<Integer> month = ConversionService.SHARED.convert(propertyMap.month, Integer)
        Optional<Integer> year = ConversionService.SHARED.convert(propertyMap.year, Integer)
        if (day.present && month.present && year.present) {
            try {
                return Optional.of(LocalDate.of(year.get(), month.get(), day.get())) // (2)
            } catch (DateTimeException e) {
                context.reject(propertyMap, e) // (3)
                return Optional.empty()
            }
        }
        return Optional.empty()
    }
}
import io.micronaut.core.convert.ConversionContext
import io.micronaut.core.convert.ConversionService
import io.micronaut.core.convert.TypeConverter
import java.time.DateTimeException
import java.time.LocalDate
import java.util.Optional
import jakarta.inject.Singleton

@Singleton
class MapToLocalDateConverter : TypeConverter<Map<*, *>, LocalDate> { // (1)
    override fun convert(propertyMap: Map<*, *>, targetType: Class<LocalDate>, context: ConversionContext): Optional<LocalDate> {
        val day = ConversionService.SHARED.convert(propertyMap["day"], Int::class.java)
        val month = ConversionService.SHARED.convert(propertyMap["month"], Int::class.java)
        val year = ConversionService.SHARED.convert(propertyMap["year"], Int::class.java)
        if (day.isPresent && month.isPresent && year.isPresent) {
            try {
                return Optional.of(LocalDate.of(year.get(), month.get(), day.get())) // (2)
            } catch (e: DateTimeException) {
                context.reject(propertyMap, e) // (3)
                return Optional.empty()
            }
        }

        return Optional.empty()
    }
}
  1. 该类实现了 TypeConverter,它有两个通用参数,一个是你要转换的类型,一个是你要转换成的类型

  2. 该实现委托默认的共享转换服务来转换用于创建 LocalDate 的 Map 中的值

  3. 如果在绑定期间发生异常,则调用 reject(..) 将附加信息传播到容器