`
fly0wings
  • 浏览: 35234 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

spring default-autowire属性引发的 No unique bean 异常

 
阅读更多
问题:
      在项目中需要加入一个新的数据源, 由于采用mybatis框架. 所以需要定义两个org.mybatis.spring.SqlSessionFactoryBean(SessionFactory). 此时,原有继承自org.mybatis.spring.support.SqlSessionDaoSupport的dao, 无法工作. 抛异常如下:
Java代码  收藏代码
  1. 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]; ......  
  2. ......  
  3. 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]  
  4. .....  

 

     

查找原因:

   1. 起初怀疑是SqlSessionDaoSupport的问题,认为他是不支持多数据源的.后来看到网上的一篇文章说 mybatis-spring:1.2.0以后为了更方便的支持多数据源,取消了SqlSessionDaoSupport. setSqlSessionFactory()的@Autowired 标注.
  2.既然SqlSessionDaoSupport 没有@Autowired ,在项目中的BaseSqlSessionDao使用了@Autowired+@Qualifier 进行了注入.那么为什么还会找到两个呢? 显然这个annotation没有起作用吗...这个问题纠结了一下午,后来,偶然间发现了在配置文件中有一个属性很别致.
Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2.   
  3.       <beans xmlns="http://www.springframework.org/schema/beans"  
  4.   
  5.          ...... /spring-context-3.0.xsd" default-autowire="byType">  
  6.   
  7.       </beans>  
  
  3.在去掉default-autowire属性后,程序正常运行.不在报上述异常了....why?
  4.后来参考了一些资料,知道了其中的知识点.即,
    1. 如果指定default-autowire属性,在加载 bean 的时候, 会按照byName/byType进行注入. 这里,需要注入的属性,是符合javabean的任何类,即setXXX的方法,都会进行注入,无论有没有通过xml/annotation来定义的.
    2. 完成之后,再进行@Autowired+@Qualifier 的注入.
    所以就能解释上述的疑问了.虽然SqlSessionDaoSupport 中没有显式的进行注入, 但是有setSqlSessionFactory()方法,并且符合javabean.那么就会按照type进行注入,所以找到两个,抛出异常.具体代码如下:  
Java代码  收藏代码
  1. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean:1080  
  2.   
  3.       if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||  
  4.         mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {  
  5.        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);  
  6.        // Add property values based on autowire by name if applicable.  
  7.        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {  
  8.         autowireByName(beanName, mbd, bw, newPvs);  
  9.        }  
  10.        // Add property values based on autowire by type if applicable.  
  11.        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {  
  12.         autowireByType(beanName, mbd, bw, newPvs);  
  13.        }  
  14.        pvs = newPvs;  
  15.       }  
  16.   
  17.     .....  
  18.     for (BeanPostProcessor bp : getBeanPostProcessors()) {  
  19.          if (bp instanceof InstantiationAwareBeanPostProcessor) {  
  20.           InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;  
  21.     //@Autowired的处理类为AutowiredAnnotationBeanPostProcessor, 它是InstantiationAwareBeanPostProcessor 的子类.  
  22.           pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);  
  23.           if (pvs == null) {  
  24.            return;  
  25.           }  
  26.          }  
  27.         }  

 

    5. 也就是说,如果指定了default-autowire ,并且指定了@Autowired, 那么这个属性有可能会被注入两次. 并且如果有一次不成功,那么就挂了.
    6. 在DefaultListableBeanFactory.doResolveDependency() 执行的时候, 还会处理一个primary属性, 可以注意一下. 如果存在一个type有多个bean的时候,而且注入是只需要一个, 那么可以通过primary=true来指定一个优先被加载的bean,来解决上面的问题.
Java代码  收藏代码
  1. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);  
  2.      if (matchingBeans.isEmpty()) {  
  3.       if (descriptor.isRequired()) {  
  4.        raiseNoSuchBeanDefinitionException(type, "", descriptor);  
  5.       }  
  6.       return null;  
  7.      }  
  8.      if (matchingBeans.size() > 1) {  
  9.       String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);  
  10.       if (primaryBeanName == null) {  
  11.        throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " +  
  12.          matchingBeans.size() + ": " + matchingBeans.keySet());  
  13.       }  
  14.       if (autowiredBeanNames != null) {  
  15.        autowiredBeanNames.add(primaryBeanName);  
  16.       }  
  17.       return matchingBeans.get(primaryBeanName);  
  18.      }  
  19.      // We have exactly one match.  
  20.      Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();  
  21.      if (autowiredBeanNames != null) {  
  22.       autowiredBeanNames.add(entry.getKey());  
  23.      }  
  24.      return entry.getValue();  
  
       
     其中的执行逻辑是:
    1. 首先根据类型找到所有可以满足条件的bean
    2. 判断bean长度,如果没有,则根据@autowired中的required属性进行判断是否抛出异常(默认为true)
    3. 如果多于一个,则尝试寻找最优的那一个,如果最优的未找到,则抛出异常
    4. 如果只有一个,则直接使用此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
分享到:
评论

相关推荐

    spring-autowire-demo.zip

    本示例"spring-autowire-demo.zip"是基于Spring 5.3.6版本,通过多个示例展示了自动装配的不同方式:default、byName、byType和constructor,帮助我们更深入理解这个功能。 首先,让我们了解什么是自动装配。自动...

    Spring--2.Spring 中的 Bean 配置-2-1

    - 如果不想通过默认构造函数创建Bean,可以使用`factory-bean`和`factory-method`来指定工厂Bean和工厂方法。 8. ** Profiles**: - Spring 3.1 引入了Profiles的概念,允许根据不同的环境配置不同的Bean。例如:...

    spring-autowire.zip

    对于没有匹配类型的bean,Spring会抛出异常。如果希望在找不到匹配bean时使用null或者默认值,可以使用`@Autowired(required = false)`。 在实际项目中,我们通常会配合@Configuration和@Bean注解来配置bean,这样...

    spring bean 属性总结

    `factory-bean`属性指向工厂Bean,而`factory-method`属性指定工厂方法。 - **depends-on属性**:指定Bean的依赖关系,确保某些Bean在其他Bean之前初始化。 #### `&lt;property&gt;`元素 `&lt;property&gt;`元素用于配置Bean...

    storm-spring-autowire:使storm支持spring的注入功能

    storm-spring-autowire 使storm支持spring的注入功能 由于storm的生命周期如下 1.在提交了一个topology之后(是在nimbus所在的机器么?), 创建spout/bolt实例(spout/bolt在storm中统称为component)并进行序列化. 2.将...

    撸一撸Spring Framework-IoC-BeanDefinition(csdn)————程序.pdf

    它解析`&lt;bean&gt;`标签,从中提取出`class`属性(定义bean的实现类)、`parent`属性(定义父bean)、以及其他的属性,如`scope`(作用域)、`abstract`(是否为抽象bean)、`lazy-init`(是否延迟初始化)、`autowire`...

    Spring-Framework-Notes-For-Professionals.pdf

    - 也可以声明性地定义Bean,并通过@Autowire注解自动注入依赖。 - Spring支持将原型作用域的Bean注入单例作用域的Bean中。 6. Spring Bean的作用域 - Spring容器中的Bean可以有不同的作用域。 - 默认作用域是...

    spring02-3

    在Spring的XML配置文件中,当一个bean定义使用`parent`属性引用另一个bean定义时,它将继承父bean的所有属性,包括初始化方法、属性注入等。这种方式可以实现配置的复用,减少重复代码,提高可维护性。例如,我们...

    Spring考试.doc

    - `default-autowire`属性在`&lt;beans&gt;`标签中设置,会影响没有单独设置`autowire`属性的`&lt;bean&gt;`标签,但不会影响其他配置文件的装配方式,每个配置文件需要单独设置。 4. **依赖注入的核心机制**: - 依赖注入...

    spring-core.pdf

    - **排除自动装配**: 可以通过设置`autowire-candidate`属性为`false`来排除某个Bean的自动装配。 - **1.4.6 方法注入**: - **查找方法注入**: 通过查找方法注入依赖项。 - **任意方法替换**: 替换现有方法的...

    第十章 Spring 配置元信息(Configuration Metadata)1

    3. **Spring 容器配置元信息**:这包括XML配置文件中的`beans`元素,它定义了容器的全局属性,如`default-lazy-init`、`default-autowire`等,这些属性影响整个容器的行为。例如,`default-lazy-init`决定了容器是否...

    spring的Bean配置说明

    11. `factory-bean` 和 `factory-method` 属性:当Bean需要通过工厂方法创建时,`factory-bean`指定工厂Bean的名称,`factory-method`指定工厂方法。 12. `depends-on` 属性:在某些特殊情况下,如需要确保某个Bean...

    Spring.pdf

    - default:使用默认方式,可以通过在beans标签中使用default-autowire属性来配置全局的自动注入规则。 - no:不使用自动注入,使用依赖注入。 scope属性用于定义Spring bean的作用域,它决定了Spring容器创建对象...

    Spring的Bean配置说明

    `factory-bean`属性指向一个已经定义的Bean,而`factory-method`则指明了工厂Bean中的哪个方法用于创建目标Bean。 - **依赖关系**: - **`depends-on`属性**:定义Bean之间的依赖关系,确保指定的Bean在当前Bean...

    maven-spring-ioc

    实例化Bean有两种方式:通过`class`属性指定构造器,或者通过`factory-bean`和`factory-method`指定工厂方法。 - **构造器实例化**: ```xml &lt;bean id="exampleBean" class="com.example.ExampleClass"&gt; &lt;!-- ...

    如何完成spring的最小化XML配置

    如果遇到多个匹配项,可以使用`primary`属性标记首选Bean,或使用`autowire-candidate`属性取消特定Bean的自动装配资格。 2. 默认自动装配 可以在`&lt;beans&gt;`根元素上设置`default-autowire`属性,为整个配置文件...

    spring-3.1.0中文版api帮助文档

    - **Autowiring策略**:通过`&lt;bean/&gt;`元素的`autowire`属性自动匹配和注入依赖。 - **@Required注解加强管理**:使用`@Required`注解确保必需的依赖被正确注入。 - **基于@Autowired或@Inject注解的Autowiring策略**...

    Spring中自动装配的4种方式

    constructor 自动装配是指通过设置 bean 标签的 autowire 属性为 constructor,Spring 将寻找与该 Bean 的构造函数各个参数类型相匹配的 Bean,通过构造函数注入进来。例如: ```xml &lt;bean id="userService" class=...

    Spring的BeanFactory的接口注解

    在Spring框架中,`BeanFactory`是工厂模式的一种实现,它负责管理容器中的Bean的生命周期与依赖注入。了解`BeanFactory`及其相关接口的功能对于掌握Spring的核心原理至关重要。 #### 二、`BeanFactory`接口的关键...

Global site tag (gtag.js) - Google Analytics