Micronaut Data 把已编译 record 的属性认成接口类型,检查协变 bridge accessor
Micronaut Data 可能把已编译 record 的协变 bridge 认成属性读取方法。Core 5.2.15 修正具体 accessor 选择,验收须保留两模块和字节码 classpath 条件。
一个带 @Embedded 的 record 在 model 模块单独编译后,repository 模块突然报“属性类型缺少 @Embeddable”,而真正的 record component 类型明明有该注解,要比较错误里的类型名与源码声明。若报错指向接口类型而非具体 record 类型,可能是编译器属性分析取到了 bridge accessor。
这个案例的关键组合是:record 已经编译成 class、放在注解处理器的 classpath 上;它实现的接口也声明同名 accessor,但返回类型更宽。例如具体 component 类型是 ResourceDetails,接口允许返回 ResourceView。Java 会保留具体返回类型的方法,并生成一个用于接口调用的 synthetic bridge。
从代码表面看只有 resource(),字节码里却可以有两种返回类型。对 model 编译产物执行:
javap -p -v -classpath model/build/classes/java/main example.BinaryRecordEntity
检查 resource() 的 descriptor 与 ACC_BRIDGE, ACC_SYNTHETIC 标记。具体 accessor 应返回 ResourceDetails;bridge 返回 ResourceView。如果实体来自 jar,就将 -classpath 换成那个 jar 的路径。
在 Core 5.2.4、Micronaut Data 5.1.4 的具体故障中,编译后的 record 经 RecordComponentElement.getAccessor() 取回了 bridge,Micronaut 的 bean property 随之使用接口类型,Data 于是认为 @Embedded 所指类型没有 @Embeddable。给接口再加一个注解会改变建模含义,掩盖属性类型被取错的问题。
Core 5.2.15 的 JavaClassElement 已增加按 component 类型选择 accessor 的处理:先检查现有 accessor 返回类型是否与 component 相同;不相同时,在同名、无参方法里找返回类型真正匹配的声明。这项处理在 Java 注解处理器侧,应通过现有版本管理更新 micronaut-inject-java 及其 Core 处理器依赖,再干净重编译 repository。
验证必须保留两模块边界。先编译 model,把产物交给 repository 的编译及注解处理 classpath,然后单独编译 repository。可按项目任务名运行:
./gradlew :model:clean :model:jar :repository:clean :repository:compileJava
不要仅把实体源码搬到 repository 后编译通过,就认为问题消失;那已经换成同一轮源码处理,不是原来的 binary record 路径。
验收除了编译成功,还应检查生成的属性元数据:resource 的属性类型与读取方法返回类型都应是 ResourceDetails,而不是 ResourceView。id() 若也通过泛型接口产生 bridge,也应保持 component 声明的具体类型。数据库写入验证仍需在项目自己的映射测试里完成,编译器属性检查不能代替真实持久化行为。
范围限于已有字节码 record 的协变 accessor 选择。如果错误确实指向未标注的具体嵌入类型,或实体根本没有 bridge,就继续检查映射注解和处理器配置,不要把所有 @Embeddable 错误都归到这个补丁。


