我遇到了一个问题,只能用我对 Spring 的 IoC 容器设施和上下文设置的基本缺乏理解来解释,所以我会要求对此进行澄清。
仅供参考,我正在维护的应用程序具有以下技术堆栈:
- Java 1.6
- 春天 2.5.6
- RichFaces 3.3.1-GA 用户界面
- Spring 框架用于 bean 管理,Spring JDBC 模块用于 DAO 支持
- Maven 用作构建管理器
- JUnit 4.4 现在作为测试引擎引入
我正在追溯(原文如此!)为应用程序编写 JUnit 测试,令我惊讶的是,我无法通过使用 setter 注入将 bean 注入测试类而不诉诸 @Autowire 表示法。
让我提供设置示例和随附的配置文件。
测试类TypeTest
非常简单:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest {
@Autowired
private IType type;
@Test
public void testFindAllTypes() {
List<Type> result;
try {
result = type.findAlltTypes();
assertNotNull(result);
} catch (Exception e) {
e.printStackTrace();
fail("Exception caught with " + e.getMessage());
}
}
}
其上下文定义在TestStackOverflowExample-context.xml
:
<context:property-placeholder location="classpath:testContext.properties" />
<context:annotation-config />
<tx:annotation-driven />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.connection.driver.class}" />
<property name="url" value="${db.connection.url}" />
<property name="username" value="${db.connection.username}" />
<property name="password" value="${db.connection.password}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="beanDAO" class="com.example.BeanDAOImpl">
<property name="ds" ref="dataSource"></property>
<property name="beanDAOTwo" ref="beanDAOTwo"></property>
</bean>
<bean id="beanDAOTwo" class="com.example.BeanDAOTwoImpl">
<property name="ds" ref="dataSource"></property>
</bean>
<bean id="type" class="com.example.TypeImpl">
<property name="beanDAO" ref="beanDAO"></property>
</bean>
TestContext.properties
位于类路径中,仅包含数据源所需的特定于 db 的数据。
这就像一个魅力,但我的问题是 - 为什么当我尝试手动连接 bean 并执行 setter 注入时它不起作用,如下所示:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest {
private IType type;
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
我在这里想念什么?这里配置的哪一部分是错误的?当我尝试通过设置器手动注入 bean 时,测试失败,因为这部分
result = type.findAlltTypes();
在运行时被解析为 null。当然,我查阅了 Spring 参考手册并尝试了各种 XML 配置组合;我所能得出的结论是,Spring 无法注入 bean,因为它以某种方式无法正确取消引用 Spring Test Context 引用,但是通过使用 @Autowired,这会“自动”发生,我真的不明白为什么会这样,因为Autowired
注释及其的JavaDocPostProcessor
类没有提到这一点。
@Autowired
另外值得添加的是仅在应用程序中使用的事实。在其他地方只执行手动接线,所以这也带来了一个问题 - 为什么在我的测试中它在那里工作而不是在这里工作?我缺少 DI 配置的哪一部分?如何@Autowired
获取 Spring Context 的引用?
编辑:我也试过这个,但结果相同:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
ctx.getBean("type");
}
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
也许还有其他想法?
EDIT2:我找到了一种方法,而无需编写自己的TestContextListener
或BeanPostProcessor
. 这出人意料地简单,事实证明我在上一次编辑时走在了正确的轨道上:
1)基于构造函数的上下文解析:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
type = ctx.getBean("type");
}
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
2)通过实现ApplicationContextAware接口:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
type = (Type) ctx.getBean("type");
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
这两种方法都正确地实例化了 bean。