使用FullyQualifiedAnnotationBeanNameGenerator启动SpringBoot时抛出NoUniqueBeanDefinitionException问题排查
这是个挺有意思的问题,我来拆解一下背后的原因和解决方案:
为什么MyRepository接口会被注册为bean?
你没给MyRepository加@Component或相关注解,它却被注册成bean,核心原因出在FullyQualifiedAnnotationBeanNameGenerator和MyBatis-Spring的交互上。
MyBatis的@MapperScan本来只会扫描带@Mapper注解的接口(也就是你的MyRepositoryImpl),并为它生成代理bean。但当你指定FullyQualifiedAnnotationBeanNameGenerator作为名称生成器时,MyBatis-Spring的扫描逻辑出现了特殊行为:它会把MyRepositoryImpl的父接口MyRepository也错误地注册为一个bean——哪怕MyRepository没有@Mapper注解。
这就导致Spring容器里出现了两个类型为MyRepository的bean:一个是MyRepositoryImpl的代理实例,另一个是莫名其妙被注册的MyRepository接口对应的bean,最终触发NoUniqueBeanDefinitionException。
为什么不用这个NameGenerator就正常?
默认的AnnotationBeanNameGenerator是用类的短名称(比如myRepositoryImpl)作为bean名称的。搭配MyBatis-Spring时,它不会触发上述的父接口错误注册逻辑——只会为带@Mapper的MyRepositoryImpl生成一个代理bean。此时容器里只有一个符合MyRepository类型的bean,依赖注入自然没问题。
解决方案
- 显式限定
@MapperScan的扫描规则:如果必须用FullyQualifiedAnnotationBeanNameGenerator,可以在@MapperScan里明确指定只扫描带@Mapper注解的接口,避免父接口被误注册:@MapperScan( nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class, annotationClass = Mapper.class // 显式指定只扫描带@Mapper的接口 ) - 调整依赖注入的类型:如果可以接受,在
MyService里直接注入MyRepositoryImpl而不是MyRepository,但这违背了面向接口编程的原则,不推荐。 - 使用默认名称生成器:如果没有特殊需求,直接去掉
nameGenerator配置,用默认的AnnotationBeanNameGenerator是最稳妥的选择。
内容的提问来源于stack exchange,提问作者avenue68

