Одной из причин является тестируемость. Скажем, у вас есть этот класс:
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"));
}
}
Как вы можете проверить этот боб? Например. как это:
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 (возможно, он должен в любом случае, но это позволяет просматривать и тестировать код отдельно от того, что делает весна).
Все еще возможно сделать то же самое при использовании 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
автоматически неправильно и его следует избегать любой ценой. Однако, если есть лучшие варианты (и они есть в большинстве случаев), используйте лучшие варианты.