You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Grails 3.3.15至4.0.13时遭遇NonUniqueBeanException求助

问题:Grails 3.3.15升级到4.0.13后启动报错NonUniqueBeanException

启动应用时抛出如下错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'grailsWebDataBinder': Injection of autowired dependencies failed; nested exception is io.micronaut.context.exceptions.NonUniqueBeanException: Multiple possible bean candidates found: [org.grails.databinding.converters.web.LocaleAwareNumberConverter, org.grails.databinding.converters.web.LocaleAwareNumberConverter]

已排查信息

  • 项目通过自定义类IdentifiablePropertyAwareGrailsWebDataBinder继承GrailsWebDataBinder,并在resources.groovy中配置替换原Bean:
    grailsWebDataBinder(IdentifiablePropertyAwareGrailsWebDataBinder, grailsApplication)
    
  • 调试发现问题源于DefaultConvertersConfiguration.java中定义的基本类型与包装类型转换器Bean,比如short和Short对应的两个Bean:
    @Bean("defaultShortConverter")
    protected LocaleAwareNumberConverter shortConverter() {
        final LocaleAwareNumberConverter converter = new LocaleAwareNumberConverter();
        converter.setLocaleResolver(localResolver);
        converter.setTargetType(Short.class);
        return converter;
    }
    
    @Bean("defaultshortConverter")
    protected LocaleAwareNumberConverter primitiveShortConverter() {
        final LocaleAwareNumberConverter converter = new LocaleAwareNumberConverter();
        converter.setLocaleResolver(localResolver);
        converter.setTargetType(short.class);
        return converter;
    }
    
  • 注释掉resources.groovy中的自定义Binder配置后,错误消失。

问题分析

你的分析方向是对的:Grails 4.x基于Micronaut,而Micronaut的Bean解析在处理大小写敏感的Bean名称时,和Grails 3.x的Spring环境存在差异。自定义grailsWebDataBinder时,自动注入依赖的转换器Bean会因为名称大小写问题被识别为同类型的多个候选,导致注入失败。

解决方案

方案1:显式指定注入的转换器Bean

修改自定义Binder的构造或注入逻辑,通过限定Bean名称明确注入目标转换器,避免模糊匹配。

在自定义Binder类中,通过@Qualifier指定Bean名称注入:

import org.grails.databinding.converters.web.LocaleAwareNumberConverter;
import jakarta.inject.Qualifier;
import org.springframework.beans.factory.annotation.Autowired;
import grails.core.GrailsApplication;

public class IdentifiablePropertyAwareGrailsWebDataBinder extends GrailsWebDataBinder {

    @Autowired
    public IdentifiablePropertyAwareGrailsWebDataBinder(Object target, String objectName,
            @Qualifier("defaultShortConverter") LocaleAwareNumberConverter shortConverter,
            @Qualifier("defaultshortConverter") LocaleAwareNumberConverter primitiveShortConverter,
            GrailsApplication grailsApplication) {
        super(target, objectName);
        // 执行自定义绑定逻辑
    }
}

或者直接在resources.groovy中手动指定依赖:

grailsWebDataBinder(IdentifiablePropertyAwareGrailsWebDataBinder, grailsApplication) {
    shortConverter = ref("defaultShortConverter")
    primitiveShortConverter = ref("defaultshortConverter")
    // 其他转换器依赖同理配置
}

方案2:使用Grails 4.x推荐的Bean替换方式(优先选择)

Grails 4.x不建议直接在resources.groovy中定义同名Bean覆盖默认实现,改用配置类注册的方式避免冲突:

  1. 创建自定义Databinding配置类:
import org.grails.databinding.GrailsWebDataBinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import grails.core.GrailsApplication;

@Configuration
public class CustomDatabindingConfig {

    @Bean
    public GrailsWebDataBinder grailsWebDataBinder(GrailsApplication grailsApplication) {
        // 注意构造方法参数要匹配父类的正确签名
        return new IdentifiablePropertyAwareGrailsWebDataBinder(grailsApplication);
    }
}
  1. 删除resources.groovy中原有的grailsWebDataBinder配置,让Micronaut自动处理依赖注入的Bean解析。

方案3:手动获取转换器Bean

如果自动注入冲突无法避免,可以在自定义Binder中手动从上下文获取指定名称的转换器:

import org.springframework.context.ApplicationContext;
import org.grails.databinding.converters.web.LocaleAwareNumberConverter;
import grails.core.GrailsApplication;

public class IdentifiablePropertyAwareGrailsWebDataBinder extends GrailsWebDataBinder {

    public IdentifiablePropertyAwareGrailsWebDataBinder(Object target, String objectName, 
            GrailsApplication grailsApplication, ApplicationContext applicationContext) {
        super(target, objectName);
        // 手动获取指定名称的转换器
        LocaleAwareNumberConverter shortConverter = applicationContext.getBean("defaultShortConverter", LocaleAwareNumberConverter.class);
        LocaleAwareNumberConverter primitiveShortConverter = applicationContext.getBean("defaultshortConverter", LocaleAwareNumberConverter.class);
        // 添加到Binder的转换器集合中
        addConverter(shortConverter);
        addConverter(primitiveShortConverter);
        // 执行自定义逻辑
    }
}

然后在resources.groovy中注入ApplicationContext:

grailsWebDataBinder(IdentifiablePropertyAwareGrailsWebDataBinder, grailsApplication, applicationContext)

内容的提问来源于stack exchange,提问作者rts_carl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 19:57:44