SpringBoot解决同名类导致的bean名冲突bean name conflicts问题
作者:野猿新一
SpringBoot同名类导致的bean名冲突bean name conflicts
问题描述
项目中有两个同名类都叫MyService,只是放在不同的包名下
package com.yeyuanxinyi; @Service() public class MyService { }
package com.yeyuanxiner; @Service() public class MyService { }
当项目启动的时候会报如下错误
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'MyService' for bean class [com.yeyuanxinyi.MyService] conflicts with existing, non-compatible bean definition of same name and class [com.yeyuanxiner.MyService]
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:348)
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:286)
at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:132)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:287)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:242)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:199)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:167)
... 13 common frames omitted
解决方法
问题分析:
由于以上两个同名类在用@Service注解的时候未指定value值,所以Spring Boot默认会以类名作为bean name,由于两个类名相同导致了bean name相同的冲突
可以通过为同名类指定不同的bean name来解决问题,上面两个同名类修改后如下
再次运行项目就不会报错了
package com.yeyuanxinyi; @Service("MyService1") public class MyService { }
package com.yeyuanxiner; @Service("MyService2") public class MyService { }
同理,以上解决方法也适用于@Component
package com.yeyuanxiner; @Component("MyComponent1") public class MyComponent { }
SpringBoot不同包结构下,同名类冲突导致服务启动失败解决方案
项目背景:
两个项目的包结构和类名都很多相同,于是开始考虑使用加一级包进行隔离,类似于这种结构
但是在启动的过程中,抛出来这样的异常:
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'nameConflict' for bean class [xom.liuyun.beannameconflict.modelB.NameConflict] conflicts with existing, non-compatible bean definition of same name and class [xom.liuyun.beannameconflict.modelA.NameConflict]
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:348) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:286) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:132) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:284) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:241) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:198) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:166) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
... 13 common frames omitted。
原因:
spring提供两种beanName生成策略,基于注解的sprong-boot默认使用的是AnnotationBeanNameGenerator,它生成beanName的策略就是,取当前类名(不是全限定类名)作为beanName。由此,如果出现不同包结构下同样的类名称,肯定会出现冲突。
解决方案如下
1. 自己写一个类实现 org.springframework.beans.factory.support.BeanNameGeneraot接口
public class UniqueNameGenerator extends AnnotationBeanNameGenerator { @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { //全限定类名 String beanName = definition.getBeanClassName(); return beanName; } }
2. 在启动类上加注解@ComponentScan(nameGenerator = UniqueNameGenerator.class)使刚才我们自定义的BeanName生成策略生效。
@SpringBootApplication @ComponentScan(nameGenerator = UniqueNameGenerator.class) public class BeanNameConflictApplication { public static void main(String[] args) { SpringApplication.run(BeanNameConflictApplication.class, args); } }
这样,问题就可以解决了。
另外解决方式
解决办法:
- 一:将其中一个实现类改为不同的名字;
- 二:将其中一个注解变更为一个name为非roleServiceImpl的注解@service(name="aaaa")。
别名:
@Autowired注解时,属性名即为默认的Bean名,如下面的logPrint就是获取beanName=logPrint的bean
@Resource(name=xxx) 直接指定Bean的name,来唯一选择匹配的bean
说明:
@Primary注解
这个注解就是为了解决当有多个bean满足注入条件时,有这个注解的实例被选中
- @Resource 指定beanName的是否会被@Primary影响
- 前面的@Autowired注解 + 属性名的方式,是按照第一节的方式选择呢,还是选择被@Primary标识的实例
- @Autowired + 随意的一个非beanName的属性,验证是否会选中@Primary标识的注解
- 根据前面的执行,因此可以知晓,选择bean的方式如下
存在@Primary注解时
- @Resource注解指定name时,根据name来查找对应的bean
- @Autowired注解,全部都用@Primary标识的注解
- @Primary注解要求唯一(非广义的唯一性,并不是指只能用一个@Primary,具体看前面)
不存在@Primary注解时
- @Resource注解指定name时,根据name来查找对应的bean
- @Autowired注解时,根据属性名去查对应的Bean,如果查不到则抛异常;如果查到,那即是它了
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。