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

存在同名类

工程中(含依赖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”)
    原文作者:zhuguowei2
    原文地址: https://segmentfault.com/a/1190000005764572
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞