如果Spring容器中拥有多个匹配UserService类型的Bean,由于Spring没有足够的信息做出取舍决策,因此会抛出UnsatisfiedDependencyException异常。假设我们采用以下传统的事务管理的配置方式对UserService进行配置,按类型匹配的自动装配机制就会引发问题:
①用于被代理的目标Bean,按类型匹配于UserService
<bean id="userServiceTarget" class="com.baobaotao.service.UserServiceImpl"> <property name="userDao" ref="userDao" /> <property name="loginLogDao" ref="loginLogDao"></property> </bean>
②通过事务代理工厂为UserServiceImpl创建的代理Bean,也按匹配于UserService
<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userServiceTarget" /> <property name="transactionAttributes"> … </property> </bean>
由于①处和②处的Bean都按类型匹配于UserService,在对DependencyInjectionCtxTest的userService属性进行自动装配将会引发问题。有两种针对该问题的解决办法:
调整配置文件,使按类型匹配于UserService的Bean仅有一个,具体有以下两个方法:
将①处的Bean作为②处的内部Bean进行装配;
使用基于注解驱动的事务管理配置机制,这样就无需在配置文件中定义两个UserService的Bean了。关于注解驱动事务管理配置的详细信息,请参见9.6小节的内容。
改变DependencyInjectionCtxTest的自动装配机制:Spring默认使用byType类型的自动装配机制,但它允许你通过setAutowireMode()的方法改变默认自动装配的机制,比如你可以调用setAutowireMode(AUTOWIRE_BY_NAME)方法启用按名称匹配的自动装配机制。AbstractDependencyInjectionSpringContextTests定义了三个代表自动装配机制类型的常量,分别说明如下:
AUTOWIRE_BY_TYPE:按类型匹配的方式进行自动装配,这个默认的机制;
AUTOWIRE_BY_NAME:按名字匹配的方式进行自动装配
AUTOWIRE_NO:不使用自动装配机制,这意味着你需要手工调用getBean()进行装配。
现在我们解决了在自动装配时,因Spring容器中存在多个匹配Bean而导致的问题,接下来让我们考察另一个自动装配的问题。
依赖检查
假设我们在DependencyInjectionCtxTest添加一个User类型的属性并提供Setter方法,而Spring容器中没有匹配该属性的Bean:
package com.baobaotao.test; … import com.baobaotao.domain.User; public class DependencyInjectionCtxTest extends AbstractDependencyInjectionSpringContextTests { private User user; public void setUser(User user) { this.user = user; } … }
猜想一下重新运行DependencyInjectionCtxTest将会发生什么情况呢?答案可能让你失望:UnsatisfiedDependencyException再次象黑幕一样降临。在默认情况下, AbstractDependencyInjectionSpringContextTests要求所有属性都能在Spring容器中找到对应Bean,否则抛出异常。
仔细思考一下,这种运行机制并非没有道理,因为既然你已经提供了Setter方法,就相当于给出了这样的暗示信息:“这个属性测试类自身创建不了,必须由外部提供”。而在使用自动装配机制的情况下,测试类属性自动从Spring容器中注入匹配的属性,一般情况下不会手工去调用Setter方法准备属性。
如果你出于一些特殊的理由,希望在采用自动装配的情况下,如果有属性未得到装配也不在乎,那么你可以在测试类构造函数中调用setDependencyCheck(false)方法达到目的:
package com.baobaotao.test; … public class DependencyInjectionCtxTest extends AbstractDependencyInjectionSpringContextTests { public DependencyInjectionCtxTest(){ setDependencyCheck(false); ①告知不进行属性依赖性检查 } … }
这个AbstractDependencyInjectionSpringContextTests就不会对测试类有些属性找不到匹配Bean而抛出异常了。