升级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覆盖默认实现,改用配置类注册的方式避免冲突:
- 创建自定义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); } }
- 删除
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
相关产品推荐
相关产品推荐

