Spring Boot R2DBC 自定义转换器类型匹配 Bug Issue (Spring Boot R2DBC Custom Converter Type Matching Bug Issue)
问题描述 (Problem Description)
在使用 Spring Boot R2DBC 框架的自定义转换器时,发现 ResolvableType.isAssignableFromResolvedPart()
方法无法正确识别匹配的泛型类型,导致类型看似相同但匹配结果为 false
。
When using custom converters in the Spring Boot R2DBC framework, it was found that the ResolvableType.isAssignableFromResolvedPart()
method cannot correctly identify matching generic types, causing types that appear identical to return false
for matching results.
环境信息(Environment Information)
- Spring Boot 版本:3.5.3
- Spring Boot Version: 3.5.3
- Spring Data R2DBC 版本:3.5.1
- Spring Data R2DBC Version: 3.5.1
- JDK 版本:21+
- JDK Version: 21+
- 语言:Kotlin
- Language: Kotlin
问题重现(Problem Reproduction)
1. 自定义转换器实现(Custom Converter Implementation)
@ReadingConverter
abstract class JsonStringToBeanConverter<T> : Converter<String, T> {
private val typeReference: TypeReference<T>
init {
val superClass = javaClass.genericSuperclass
val type = (superClass as ParameterizedType).actualTypeArguments[0]
typeReference = object : TypeReference<T>() {
override fun getType(): Type {
return type
}
}
}
override fun convert(source: String): T {
return Json.mapper().readValue(source, typeReference)
}
}
2. R2DBC 配置(R2DBC Configuration)
@Configuration
class R2dbcConfiguration : AbstractR2dbcConfiguration() {
override fun getCustomConverters(): MutableList<Any> {
return mutableListOf(
object : BeanToJsonStringConverter<Map<String, List<String>>>() {},
object : JsonStringToBeanConverter<Map<String, List<String>>>() {}
)
}
}
3. 实体类字段定义(Entity Class Field Definition)
@Table("part_time_job_posting")
class PartTimeJobPostingPo(
@Column("jd_content")
var jdContent: Map<String, List<String>>,
// ... 其他字段
)
4. 问题验证代码(Problem Verification Code)
fun main() {
val converter = object : JsonStringToBeanConverter<Map<String, List<String>>>() {}
val converterClass = converter.javaClass
val genericIfc = Converter::class.java
val resolvableType = ResolvableType.forClass(converterClass).`as`(genericIfc)
val generics = resolvableType.getGenerics()
val sourceType = generics[0] // String
val targetType = generics[1] // Map<String, List<String>>
val declaredField = PartTimeJobPostingPo::class.java.getDeclaredField("jdContent")
val fieldType = ResolvableType.forField(declaredField)
println("源类型: $sourceType")
println("目标类型: $targetType")
println("字段类型: $fieldType")
println("类型匹配结果: ${fieldType.isAssignableFromResolvedPart(targetType)}")
}
5. 实际输出结果(Actual Output Result)
源类型: java.lang.String
目标类型: java.util.Map<java.lang.String, java.util.List<java.lang.String>>
字段类型: java.util.Map<java.lang.String, java.util.List<java.lang.String>>
类型匹配结果: false
6. 期望结果(Expected Result)
类型匹配结果: true
Type Matching Result: true
相关 Bug:TypeDescriptor 泛型信息丢失(Related Bug: TypeDescriptor Generic Information Loss)
问题描述(Problem Description)
在 org.springframework.data.relational.core.conversion.MappingRelationalConverter#createTypeDescriptor
方法中,当处理多层嵌套泛型类型时会导致部分泛型信息丢失,这进一步加剧了自定义转换器的类型匹配问题。
In the org.springframework.data.relational.core.conversion.MappingRelationalConverter#createTypeDescriptor
method, when processing multi-level nested generic types, some generic information is lost, which further exacerbates the type matching problems of custom converters.
问题验证(Problem Verification)
让我们通过实际代码来验证这个问题:
Let's verify this issue through actual code:
org.springframework.data.relational.core.conversion.MappingRelationalConverter#createTypeDescriptor
private static TypeDescriptor createTypeDescriptor(TypeInformation<?> type) {
List<TypeInformation<?>> typeArguments = type.getTypeArguments();
Class<?>[] generics = new Class[typeArguments.size()];
for (int i = 0; i < typeArguments.size(); i++) {
generics[i] = typeArguments.get(i).getType();
}
return new TypeDescriptor(ResolvableType.forClassWithGenerics(type.getType(), generics), type.getType(), null);
}
fun testTypeDescriptorGenericLoss() {
// 模拟 MappingRelationalConverter 的当前实现
val field = PartTimeJobPostingPo::class.java.getDeclaredField("jdContent")
val typeInfo = ClassTypeInformation.from(field.genericType)
// 当前的实现可能存在泛型信息丢失
val currentTypeDescriptor = createTypeDescriptor(typeInfo)
// 建议的修复实现
val fixedTypeDescriptor = typeInfo.toTypeDescriptor()
println("当前实现的 TypeDescriptor: ${currentTypeDescriptor.source}")
println("修复后的 TypeDescriptor: ${fixedTypeDescriptor.source}")
}
预期输出问题(Expected Output Issue)
当前实现的 TypeDescriptor: java.util.Map<java.lang.String, java.util.List<?>>
修复后的 TypeDescriptor: java.util.Map<java.lang.String, java.util.List<java.lang.String>>