同名不同包的Spring Service Bean用Qualifier仍报错求助
嘿,我来帮你解决这个Spring注入的问题!你遇到的这个异常,核心原因是两个同名的UserService类在Bean注册阶段就冲突了——哪怕你加了@Qualifier也没用,因为@Qualifier是注入时用来区分Bean的标签,而注册的时候Spring已经因为默认Bean名称重复炸了。
先理清楚你的场景:
你的代码情况
package1下的服务类
package package1; @Service @Qualifier("kb") public class UserService { ... }
package2下的服务类
package package2; @Service @Qualifier("user") public class UserService { ... }
注入代码
@Autowired @Qualifier("user") package2.UserService p2; @Autowired @Qualifier("kb") package1.UserService p1;
抛出的异常
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [boot.Application]; nested exception is org.springframework.context.annotati...
为啥会出问题?
Spring默认会把类名首字母小写作为Bean的默认名称,比如这两个UserService,不管在哪个包,默认Bean名都是userService。当Spring扫描到这两个类时,会发现Bean名称重复,直接在注册Bean定义的时候就报错了,根本到不了注入那一步。@Qualifier是注入阶段用的标签,管不了注册阶段的冲突。
两种解决办法,任你选
方法一:直接给@Service指定唯一Bean名
最简单的方式,就是在@Service注解里手动设置每个Bean的唯一名称,替代默认的类名小写:
// package1的UserService,指定Bean名为kbUserService package package1; @Service("kbUserService") public class UserService { ... } // package2的UserService,指定Bean名为userUserService package package2; @Service("userUserService") public class UserService { ... }
之后注入的时候,用@Qualifier指定这个名称就行,和你原来的写法兼容:
@Autowired @Qualifier("userUserService") package2.UserService p2; @Autowired @Qualifier("kbUserService") package1.UserService p1;
当然,你也可以用@Resource(name = "userUserService")来注入,效果是一样的。
方法二:自定义Bean名称生成策略(适合大量同名类场景)
如果你的项目里有很多这种跨包同名的类,一个个手动指定Bean名太麻烦,可以自定义一个Bean名称生成器,让它把包名也加到Bean名称里,确保唯一性。
先写一个自定义生成器:
public class PackagePrefixedBeanNameGenerator extends AnnotationBeanNameGenerator { @Override protected String buildDefaultBeanName(BeanDefinition definition) { // 把全类名的点换成横杠,转成小写,比如package1.UserService变成package1-userservice String fullClassName = definition.getBeanClassName(); return fullClassName.replace('.', '-').toLowerCase(); } }
然后在Spring Boot的启动类上,用@ComponentScan指定这个生成器:
@SpringBootApplication @ComponentScan(nameGenerator = PackagePrefixedBeanNameGenerator.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
这样两个UserService的Bean名就变成了package1-userservice和package2-userservice,不会再冲突。之后你依然可以用原来的@Qualifier注解来注入,因为@Qualifier的优先级比Bean名称高。
小提醒
记住哦:@Qualifier是给注入时用的筛选标签,而Bean的注册名称是Spring内部识别Bean的唯一标识之一。注册阶段冲突了,注入阶段根本没机会执行,所以先解决注册时的名称冲突才是关键!
内容的提问来源于stack exchange,提问作者JGleason

