Uma das razões é a testabilidade. Digamos que você tenha esta classe:
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"));
}
}
Como você pode testar esse bean? Por exemplo:
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);
}
}
Fácil né?
Enquanto você ainda depende do Spring (devido às anotações), você pode remover sua dependência do Spring sem alterar nenhum código (apenas as definições de anotação) e o desenvolvedor de teste não precisa saber nada sobre como o Spring funciona (talvez ele deva, de qualquer maneira, mas permite revisar e testar o código separadamente do que a primavera faz).
Ainda é possível fazer o mesmo ao usar o ApplicationContext. No entanto, você precisa zombar de ApplicationContext
qual é uma interface enorme. Você precisa de uma implementação fictícia ou pode usar uma estrutura de simulação como o 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);
}
}
Essa é uma possibilidade, mas acho que a maioria das pessoas concorda que a primeira opção é mais elegante e simplifica o teste.
A única opção que realmente é um problema é esta:
@Component
class MyBean {
@Autowired
MyBean(StringOutput out) {
out.print(new HttpLoader().load("http://stackoverflow.com"));
}
}
Testar isso exige grandes esforços ou seu bean tentará se conectar ao stackoverflow em cada teste. E assim que houver uma falha na rede (ou os administradores do stackoverflow o bloquearem devido à taxa de acesso excessiva), você terá testes aleatórios com falha.
Portanto, como conclusão, não diria que o uso ApplicationContext
direto é automaticamente errado e deve ser evitado a todo custo. No entanto, se houver melhores opções (e na maioria dos casos), use as melhores opções.
new MyOtherClass()
objeto? Eu sei sobre @Autowired, mas eu só já é usado em campos e quebra emnew MyOtherClass()
..