其中一个原因是可测试性。假设你有这个课程:
interface HttpLoader {
String load(String url);
}
interface StringOutput {
void print(String txt);
}
@Component
class MyBean {
@Autowired
MyBean(HttpLoader loader, StringOutput out) {
out.print(loader.load("http://stackoverflow.com"));
}
}
如何测试这个 bean?例如。像这样:
class MyBeanTest {
public void creatingMyBean_writesStackoverflowPageToOutput() {
// setup
String stackOverflowHtml = "dummy";
StringBuilder result = new StringBuilder();
// execution
new MyBean(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get, result::append);
// evaluation
assertEquals(result.toString(), stackOverflowHtml);
}
}
简单吧?
虽然您仍然依赖 Spring(由于注释),但您可以在不更改任何代码(仅注释定义)的情况下移除对 Spring 的依赖,并且测试开发人员不需要了解 Spring 的工作原理(也许他应该无论如何,但它允许与 spring 分开审查和测试代码)。
在使用 ApplicationContext 时仍然可以这样做。但是,您需要模拟ApplicationContext,这是一个巨大的界面。您要么需要一个虚拟实现,要么可以使用 Mockito 等模拟框架:
@Component
class MyBean {
@Autowired
MyBean(ApplicationContext context) {
HttpLoader loader = context.getBean(HttpLoader.class);
StringOutput out = context.getBean(StringOutput.class);
out.print(loader.load("http://stackoverflow.com"));
}
}
class MyBeanTest {
public void creatingMyBean_writesStackoverflowPageToOutput() {
// setup
String stackOverflowHtml = "dummy";
StringBuilder result = new StringBuilder();
ApplicationContext context = Mockito.mock(ApplicationContext.class);
Mockito.when(context.getBean(HttpLoader.class))
.thenReturn(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get);
Mockito.when(context.getBean(StringOutput.class)).thenReturn(result::append);
// execution
new MyBean(context);
// evaluation
assertEquals(result.toString(), stackOverflowHtml);
}
}
这很有可能,但我想大多数人都会同意第一个选项更优雅,并且使测试更简单。
唯一真正有问题的选项是这个:
@Component
class MyBean {
@Autowired
MyBean(StringOutput out) {
out.print(new HttpLoader().load("http://stackoverflow.com"));
}
}
对此进行测试需要付出巨大的努力,否则您的 bean 将尝试在每个测试中连接到 stackoverflow。一旦您出现网络故障(或者由于访问速率过高而导致 stackoverflow 的管理员阻止您),您将随机出现测试失败。
因此,作为结论,我不会说直接使用ApplicationContext 是自动错误的,应该不惜一切代价避免。但是,如果有更好的选择(并且在大多数情况下都有),那么请使用更好的选择。