关于classpath中有多个同名类或一个接口有多个实现类Spring启动失败总结

代码吟游使
• 阅读 7570

存在同名类

工程中(含依赖jar包) 若有两个同名的类,Spring启动时会报错 如下所示

main
├── java
│   └── com
│           ├── bar
│           │   └── service
│           │       └── FooService.java
│           └── foo
│               └── service
│                   └── FooService.java

工程中含两个同名的FooService 启动时会报错

Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'fooService' for bean class [com.foo.service.FooService] conflicts with existing, non-compatible bean definition of same name and class [com.bar.service.FooService]
    at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:314)

解决方法

给任一FooService取个别名即可 如

@Service("anotherFooService")

此时可以正常启动。并且注入(@Autowired)的时候 也无需显式指定别名 即正常注入即可 如下所示

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/ApplicationContextTest.xml" })
public class FooService1Test{
    @Autowired
    private FooService service; // 使用的是foo包下的
    @Test
    public void test_doSomething(){
        String s = service.doSomething();
        Assert.assertEquals("com.foo.service.FooService",s);
    }
}
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/ApplicationContextTest.xml"})
public class FooService2Test {
    @Autowired
    private FooService service; // 使用的是bar目录下的
 
    @Test
    public void test_doSomething() {
        String s = service.doSomething();
        Assert.assertEquals("com.bar.service.FooService", s);
    }
}

上面两个测试均能测试通过。 可见无需显式指定别名。该怎么用就怎么用。

补充

下面的这种情况就不存在上述的冲突异常

src
├── main
│   ├── java
│   │   └── com
│   │           ├── bar
│   │           │   └── service
│   │           │       ├── TokenService.java #接口
│   │           │       └── TokenServiceImpl.java #实现类
│   │           ├── foo
│   │           │   └── service
│   │           │       └── TokenService.java #普通类

因为分别是两个不同的bean name, 一个是tokenService, 一个是tokenServiceImpl。 如下所示

ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring/ApplicationContextTest.xml");
Object tokenServiceImpl =  classPathXmlApplicationContext.getBean("tokenServiceImpl");
Assert.assertEquals("com.bar.service.TokenServiceImpl", tokenServiceImpl.getClass().getName());
 
Object tokenService = classPathXmlApplicationContext.getBean("tokenService");
Assert.assertEquals("com.foo.service.TokenService", tokenService.getClass().getName());

什么情况下需要显式指定别名呢?

存在多个实现类

一个接口有两个实现 如下所示

  main/java
└── com
        ├── bar
        │   └── service
        │       ├── FooBarServiceImpl2.java
        ├── foo
        │   └── service
        │       ├── FooBarServiceImpl1.java
        └── service
            └── IFooBarService.java

注入的时候使用的是接口名称 如下所示

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/ApplicationContextTest.xml" })
public class FooBarServiceTest {
    @Autowired
    private IFooBarService service; //使用的是接口名称
    @Test
    public void test_doSomething(){
    }
}

此时Spring启动会报错

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.service.IFooBarService] is defined: expected single matching bean but found 2: fooBarServiceImpl2,fooBarServiceImpl1
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:970)

这种情况下需要显式指定别名 如下所示

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/ApplicationContextTest.xml" })
public class FooBarServiceTest {
    @Autowired
    @Qualifier("fooBarServiceImpl2")
    private IFooBarService service;
    @Test
    public void test_doSomething(){
        String s = service.doSomething();
        Assert.assertEquals("com.bar.service.FooBarServiceImpl2",s);
    }
}

补充

若同一个类, 被Spring加载了两次且分别指定了不同名称, 这时也需显式使用@Qualifier 如配置文件中同一类配置了两个bean

<bean id="tokenServiceImpl" class="com.bar.service.TokenServiceImpl"></bean>
<bean id="anotherTokenService" class="com.bar.service.TokenServiceImpl"></bean>

注入时若未显式指定别名的话,

@Autowired
private TokenService tokenService;

启动会报错

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.bar.service.TokenService] is defined: expected single matching bean but found 2: tokenServiceImpl,anotherTokenService

总结

异常 解释 解决
ConflictingBeanDefinitionException 表示类名冲突了 给任一类指定一个别名 如@Service("anotherFooService")
NoUniqueBeanDefinitionException 表示有多个候选类可用 注入时显式指定用哪一个 如 @Autowired @Qualifier("fooBarServiceImpl2")
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
java不同的包下相同的类名的问题与解决办法
Java中的类以包进行分类组织,当程序中需要用到某个包下的类时,可以以该类的全限定名进行引用。这样,不同的包中的类就可以同名,不会产生混淆。但是这样就可能导致引用的时候会产生一些问题。第一个问题,是Spring中自动注入的问题。Spring自动注入不同包下的相同类名的类会有点问题。Spring并不支持不同包下的类名相同的设定。这是因为默认的S
Spring竟然可以创建“重复”名称的bean?—一次项目中存在多个bean名称重复问题的排查
众所周知,在Spring中时不能够创建两个名称相同的bean的,否则会在启动时报错:但是我却在我们的spring项目中发现了两个相同名称的bean,并且项目也可以正常启动,对应的bean也可以正常使用。
Bill78 Bill78
4年前
Python的新式类和旧式类
概述:Python中支持多继承,也就是一个子类可以继承多个父类/基类。当一个调用一个自身没有定义的属性时,它是按照何种顺序去父类中寻找的呢?尤其是当众多父类中都包含有同名的属性,这就涉及到新式类和经典类的区别。多继承:classFood(object):23def__init__(self,name,col
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
SpringBoot19 之环境变量读取和属性对象的绑定
凡是被Spring管理的类,实现接口EnvironmentAware重写方法setEnvironment可以在工程启动时,获取到系统环境变量和application配置文件中的变量。@ConfigurationpublicclassMyEnvironmentAwareimplementsEnvironmentAware{/
Wesley13 Wesley13
3年前
JAVA初学笔记&宋红康JAVA基础、高级篇(其九)
关于类与方法的使用技巧方法重载定义:同一个类中,允许多个同名方法,只需所传递的参数类型不同即可(类似于路由系统)使用:根据传递的类型自动区分到对应的方法值传递多个实参传递JDK5.0之前:publicstaticvoidtest(inta,Str
Easter79 Easter79
3年前
SpringBoot19 之环境变量读取和属性对象的绑定
凡是被Spring管理的类,实现接口EnvironmentAware重写方法setEnvironment可以在工程启动时,获取到系统环境变量和application配置文件中的变量。@ConfigurationpublicclassMyEnvironmentAwareimplementsEnvironmentAware{/
小万哥 小万哥
1年前
深入理解 Java 方法重载与递归应用
Java方法重载方法重载允许在同一个类中定义多个具有相同名称的方法,但参数列表必须不同。语法:javareturnTypemethodName(parameter1,parameter2,...,parameterN)//方法体示例:javapublicc