Spring JUnit4手动/自动接线困境

我遇到了一个问题,这个问题只能解释为我对Spring的IoC容器设施和上下文设置缺乏了解,所以我会要求澄清一下。

仅供参考,我正在维护的应用程序具有以下堆栈技术:

  • Java 1.6
  • spring2.5.6
  • RichFaces 3.3.1-GA UI
  • Spring框架用于bean管理,Spring JDBC模块用于DAO支持
  • Maven用作构建管理器
  • JUnit 4.4现在作为测试引擎引入

我追溯(sic!)为应用程序编写JUnit测试,令我感到惊讶的是,我无法通过使用setter注入将bean注入测试类而不使用@Autowire表示法。

让我提供一个示例和附带的配置文件。

测试类TypeTest非常简单:

 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class TypeTest { @Autowired private IType type; @Test public void testFindAllTypes() { List result; try { result = type.findAlltTypes(); assertNotNull(result); } catch (Exception e) { e.printStackTrace(); fail("Exception caught with " + e.getMessage()); } } } 

其上下文在TestStackOverflowExample-context.xml定义:

                       

TestContext.properties位于类路径中,仅包含数据源所需的特定于数据库的数据。

这就像一个魅力,但我的问题是 – 当我尝试手动连接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... } } 

我在这里想念的是什么? 配置的哪一部分在这里有误? 当我尝试通过setter手动注入bean时,测试失败,因为这部分

 result = type.findAlltTypes(); 

在运行时被解析为null。 当然,我参考了Spring参考手册并尝试了各种XML配置组合; 我可以得出的结论是,Spring无法注入bean,因为它无法正确地取消引用Spring Test Context引用但是使用@Autowired这种情况“自动地”发生了,我真的不明白为什么会这样,因为JavaDoc同时使用了Autowired注释和它的PostProcessor类没有提到这一点。

另外值得补充的是@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:我找到了一种不用编写自己的TestContextListenerBeanPostProcessor 。 这真是太简单了,事实certificate我在上一次编辑时走在正确的轨道上:

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。

如果您查看org.springframework.test.context.support.DependencyInjectionTestExecutionListener的源代码,您将看到以下方法(为了清晰起见,格式化和注释):

 protected void injectDependencies(final TestContext testContext) throws Exception { Object bean = testContext.getTestInstance(); AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext() .getAutowireCapableBeanFactory(); beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, // no autowiring!!!!!!!! false ); beanFactory.initializeBean(bean, testContext.getTestClass().getName()); // but here, bean post processors are run testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE); } 

所以测试对象是没有自动布线的bean。 但是, @AutoWired@Resource等不使用自动assembly机制,它们使用BeanPostProcessor 。 因此,当且仅当使用注释时(或者如果您注册其他一些BeanPostProcessor来执行注释),依赖项将被注入。

(上面的代码来自Spring 3.0.x,但我敢打赌它在2.5.x中是相同的)