存在同名类
工程中(含依赖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”) |