Micronaut 代理启动报重复 toString,核对惰性代理的生成方法
Micronaut 惰性代理重复生成 toString 可使 class 加载失败。Core 5.2.15 包含生成条件修补,更新处理器后须重编译代理并验证原有方法行为。
Micronaut 应用还没执行到业务方法,就在加载 $Definition$Intercepted 时失败,错误里出现:
ClassFormatError: Duplicate method name "toString"
with signature "()Ljava.lang.String;"
如果出错类是 @Refreshable 等惰性作用域代理,而目标类恰好自己声明了 toString(),应检查代理生成器。Java 源文件只有一个方法,也可能生成包含两个同签名方法的 class。删除日志中的 bean.toString() 调用不能修复已经生成的非法字节码。
一个足够小的排查对象如下:
import io.micronaut.runtime.context.scope.Refreshable;
@Refreshable
public class RefreshableBean {
@Override
public String toString() {
return "refreshable";
}
}
这个组合在 Micronaut Core 5.1.10、平台 5.1.0 的环境有明确故障案例。Lombok 生成的方法也算目标类的 toString();检查源码时没有手写方法,不等于编译后没有。范围应收窄到实际报错的惰性代理,不把所有 AOP 启动失败都归到这里。
生成器原本会给惰性代理补一个转发到目标对象的 toString()。当同一个方法已经进入拦截方法列表时,再补一次就产生重复签名。目标类继承了 final toString() 时也不能再生成覆盖方法。
Core 5.2.15 已包含相应判断:已有被拦截的无参 toString(),或目标类型存在 final 的无参 toString(),便不再补这个方法。更新时要把注解处理器和运行依赖一起纳入 Core 版本管理,再重新生成代理。使用 Gradle、直接管理 Core BOM 的项目可以采用以下形式;已经由平台统一管理版本的项目,应在现有管理入口调整,避免叠加互相覆盖的 BOM。
dependencies {
annotationProcessor(platform("io.micronaut:micronaut-core-bom:5.2.15"))
annotationProcessor("io.micronaut:micronaut-inject-java")
implementation(platform("io.micronaut:micronaut-core-bom:5.2.15"))
implementation("io.micronaut:micronaut-runtime")
}
随后执行 ./gradlew clean test。只替换运行时 jar 会留下旧处理器生成的代理;只重启应用也不会重写 class。
验收要走到取 bean 和调用方法这一步:启动上下文,取得 RefreshableBean,断言 bean.toString() 等于 refreshable,然后正常关闭上下文。还有继承方法的项目,应分别覆盖父类普通方法、父类 final 方法及默认 Object.toString(),确认代理没有再增加冲突方法。
暂时不能升级且这个方法只是调试输出时,可以移除目标类的 toString() 后干净重编译;但它会改变对象的文本表示,不能当成无副作用的通用修复。第三方类型或有业务约定的文本输出,更适合修正生成器版本并保留原有方法。


