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:我找到了一种不用编写自己的TestContextListener
或BeanPostProcessor
。 这真是太简单了,事实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中是相同的)