问题:
在项目中需要加入一个新的数据源, 由于采用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
相关推荐
本示例"spring-autowire-demo.zip"是基于Spring 5.3.6版本,通过多个示例展示了自动装配的不同方式:default、byName、byType和constructor,帮助我们更深入理解这个功能。 首先,让我们了解什么是自动装配。自动...
- 如果不想通过默认构造函数创建Bean,可以使用`factory-bean`和`factory-method`来指定工厂Bean和工厂方法。 8. ** Profiles**: - Spring 3.1 引入了Profiles的概念,允许根据不同的环境配置不同的Bean。例如:...
对于没有匹配类型的bean,Spring会抛出异常。如果希望在找不到匹配bean时使用null或者默认值,可以使用`@Autowired(required = false)`。 在实际项目中,我们通常会配合@Configuration和@Bean注解来配置bean,这样...
`factory-bean`属性指向工厂Bean,而`factory-method`属性指定工厂方法。 - **depends-on属性**:指定Bean的依赖关系,确保某些Bean在其他Bean之前初始化。 #### `<property>`元素 `<property>`元素用于配置Bean...
storm-spring-autowire 使storm支持spring的注入功能 由于storm的生命周期如下 1.在提交了一个topology之后(是在nimbus所在的机器么?), 创建spout/bolt实例(spout/bolt在storm中统称为component)并进行序列化. 2.将...
它解析`<bean>`标签,从中提取出`class`属性(定义bean的实现类)、`parent`属性(定义父bean)、以及其他的属性,如`scope`(作用域)、`abstract`(是否为抽象bean)、`lazy-init`(是否延迟初始化)、`autowire`...
- 也可以声明性地定义Bean,并通过@Autowire注解自动注入依赖。 - Spring支持将原型作用域的Bean注入单例作用域的Bean中。 6. Spring Bean的作用域 - Spring容器中的Bean可以有不同的作用域。 - 默认作用域是...
在Spring的XML配置文件中,当一个bean定义使用`parent`属性引用另一个bean定义时,它将继承父bean的所有属性,包括初始化方法、属性注入等。这种方式可以实现配置的复用,减少重复代码,提高可维护性。例如,我们...
- `default-autowire`属性在`<beans>`标签中设置,会影响没有单独设置`autowire`属性的`<bean>`标签,但不会影响其他配置文件的装配方式,每个配置文件需要单独设置。 4. **依赖注入的核心机制**: - 依赖注入...
- **排除自动装配**: 可以通过设置`autowire-candidate`属性为`false`来排除某个Bean的自动装配。 - **1.4.6 方法注入**: - **查找方法注入**: 通过查找方法注入依赖项。 - **任意方法替换**: 替换现有方法的...
3. **Spring 容器配置元信息**:这包括XML配置文件中的`beans`元素,它定义了容器的全局属性,如`default-lazy-init`、`default-autowire`等,这些属性影响整个容器的行为。例如,`default-lazy-init`决定了容器是否...
11. `factory-bean` 和 `factory-method` 属性:当Bean需要通过工厂方法创建时,`factory-bean`指定工厂Bean的名称,`factory-method`指定工厂方法。 12. `depends-on` 属性:在某些特殊情况下,如需要确保某个Bean...
- default:使用默认方式,可以通过在beans标签中使用default-autowire属性来配置全局的自动注入规则。 - no:不使用自动注入,使用依赖注入。 scope属性用于定义Spring bean的作用域,它决定了Spring容器创建对象...
`factory-bean`属性指向一个已经定义的Bean,而`factory-method`则指明了工厂Bean中的哪个方法用于创建目标Bean。 - **依赖关系**: - **`depends-on`属性**:定义Bean之间的依赖关系,确保指定的Bean在当前Bean...
实例化Bean有两种方式:通过`class`属性指定构造器,或者通过`factory-bean`和`factory-method`指定工厂方法。 - **构造器实例化**: ```xml <bean id="exampleBean" class="com.example.ExampleClass"> <!-- ...
如果遇到多个匹配项,可以使用`primary`属性标记首选Bean,或使用`autowire-candidate`属性取消特定Bean的自动装配资格。 2. 默认自动装配 可以在`<beans>`根元素上设置`default-autowire`属性,为整个配置文件...
- **Autowiring策略**:通过`<bean/>`元素的`autowire`属性自动匹配和注入依赖。 - **@Required注解加强管理**:使用`@Required`注解确保必需的依赖被正确注入。 - **基于@Autowired或@Inject注解的Autowiring策略**...
constructor 自动装配是指通过设置 bean 标签的 autowire 属性为 constructor,Spring 将寻找与该 Bean 的构造函数各个参数类型相匹配的 Bean,通过构造函数注入进来。例如: ```xml <bean id="userService" class=...
在Spring框架中,`BeanFactory`是工厂模式的一种实现,它负责管理容器中的Bean的生命周期与依赖注入。了解`BeanFactory`及其相关接口的功能对于掌握Spring的核心原理至关重要。 #### 二、`BeanFactory`接口的关键...