问题:
在项目中需要加入一个新的数据源, 由于采用mybatis框架. 所以需要定义两个org.mybatis.spring.SqlSessionFactoryBean(SessionFactory). 此时,原有继承自org.mybatis.spring.support.SqlSessionDaoSupport的dao, 无法工作. 抛异常如下:
- org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b2cArriveMappingDao' defined in file [......\B2cArriveMappingDao.class]: Unsatisfied dependency expressed through bean property 'sqlSessionTemplate': : No unique bean of type [org.mybatis.spring.SqlSessionTemplate] is defined: expected single matching bean but found 2: [sqlSession, vacationSqlSession]; ......
- ......
- Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.mybatis.spring.SqlSessionTemplate] is defined: expected single matching bean but found 2: [sqlSession, vacationSqlSession]
- .....
查找原因:
1. 起初怀疑是SqlSessionDaoSupport的问题,认为他是不支持多数据源的.后来看到网上的一篇文章说 mybatis-spring:1.2.0以后为了更方便的支持多数据源,取消了SqlSessionDaoSupport.
setSqlSessionFactory()的
@Autowired
标注.
2.既然SqlSessionDaoSupport
没有@
Autowired
,在项目中的BaseSqlSessionDao使用了@
Autowired+@Qualifier
进行了注入.那么为什么还会找到两个呢? 显然这个annotation没有起作用吗...这个问题纠结了一下午,后来,偶然间发现了在配置文件中有一个属性很别致.
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- ...... /spring-context-3.0.xsd" default-autowire="byType">
- </beans>
3.在去掉
default-autowire属性后,程序正常运行.不在报上述异常了....why?
4.后来参考了一些资料,知道了其中的知识点.即,
1. 如果指定
default-autowire属性,在加载 bean 的时候, 会按照byName/byType进行注入. 这里,需要注入的属性,是符合javabean的任何类,即setXXX的方法,都会进行注入,无论有没有通过xml/annotation来定义的.
2. 完成之后,再进行
@
Autowired+@Qualifier
的注入.
所以就能解释上述的疑问了.虽然SqlSessionDaoSupport 中没有显式的进行注入, 但是
有setSqlSessionFactory()方法,并且符合javabean.那么就会按照type进行注入,所以找到两个,抛出异常.具体代码如下:
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean:1080
- if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
- mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
- MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
- // Add property values based on autowire by name if applicable.
- if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
- autowireByName(beanName, mbd, bw, newPvs);
- }
- // Add property values based on autowire by type if applicable.
- if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
- autowireByType(beanName, mbd, bw, newPvs);
- }
- pvs = newPvs;
- }
- .....
- for (BeanPostProcessor bp : getBeanPostProcessors()) {
- if (bp instanceof InstantiationAwareBeanPostProcessor) {
- InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
- //@Autowired的处理类为AutowiredAnnotationBeanPostProcessor, 它是InstantiationAwareBeanPostProcessor 的子类.
- pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
- if (pvs == null) {
- return;
- }
- }
- }
5. 也就是说,如果指定了
default-autowire ,并且指定了
@
Autowired, 那么这个属性有可能会被注入两次. 并且如果有一次不成功,那么就挂了.
6. 在DefaultListableBeanFactory.doResolveDependency() 执行的时候, 还会处理一个primary属性, 可以注意一下. 如果存在一个type有多个bean的时候,而且注入是只需要一个, 那么可以通过primary=true来指定一个优先被加载的bean,来解决上面的问题.
- Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
- if (matchingBeans.isEmpty()) {
- if (descriptor.isRequired()) {
- raiseNoSuchBeanDefinitionException(type, "", descriptor);
- }
- return null;
- }
- if (matchingBeans.size() > 1) {
- String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
- if (primaryBeanName == null) {
- throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " +
- matchingBeans.size() + ": " + matchingBeans.keySet());
- }
- if (autowiredBeanNames != null) {
- autowiredBeanNames.add(primaryBeanName);
- }
- return matchingBeans.get(primaryBeanName);
- }
- // We have exactly one match.
- Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
- if (autowiredBeanNames != null) {
- autowiredBeanNames.add(entry.getKey());
- }
- return entry.getValue();
其中的执行逻辑是:
-
- 首先根据类型找到所有可以满足条件的bean
- 判断bean长度,如果没有,则根据@autowired中的required属性进行判断是否抛出异常(默认为true)
- 如果多于一个,则尝试寻找最优的那一个,如果最优的未找到,则抛出异常
- 如果只有一个,则直接使用此bean
7. primary属性,对
@
Autowired的处理起效,对default-autowire处理时同样起效.
8.
除了primary属性显式的设置优先级,
还有有个隐式的优先级,在 determinePrimaryCandidate方法中,会检查
@
Autowired的属性名称,如果属性名称和bean的alias一样,则会优先加载.不会报错.
解决办法:
方案1. 去掉
default-autowire
="byType",使用
@
Autowired+@Qualifier.
方案
2. 在多个同type的bean中,指定一个bean标注primary=true.
参考:
http://www.iflym.com/index.php/code/201211070001.html
http://www.oschina.net/code/piece_full?code=21550#35968