Jednym z powodów jest testowalność. Powiedz, że masz tę klasę:
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"));
}
}
Jak możesz przetestować tę fasolę? Np. Tak:
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);
}
}
Łatwe, prawda?
Chociaż nadal jesteś zależny od Springa (z powodu adnotacji), możesz usunąć swoją zależność od wiosny bez zmiany żadnego kodu (tylko definicje adnotacji), a programista testów nie musi wiedzieć nic o tym, jak działa wiosna (może i tak powinien, ale pozwala przejrzeć i przetestować kod niezależnie od tego, co robi wiosna).
Nadal można to zrobić podczas korzystania z ApplicationContext. Jednak musisz wyśmiewać ApplicationContext
co jest ogromnym interfejsem. Potrzebujesz albo fałszywej implementacji, albo możesz użyć frameworku, takiego jak 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);
}
}
Jest to całkiem możliwe, ale myślę, że większość ludzi zgodzi się, że pierwsza opcja jest bardziej elegancka i upraszcza test.
Jedyną opcją, która naprawdę stanowi problem, jest ta:
@Component
class MyBean {
@Autowired
MyBean(StringOutput out) {
out.print(new HttpLoader().load("http://stackoverflow.com"));
}
}
Testowanie tego wymaga ogromnego wysiłku lub twoja fasola będzie próbowała połączyć się z przepełnieniem stosu w każdym teście. A gdy tylko dojdzie do awarii sieci (lub administratorzy blokują przepływ stosu blokując Cię z powodu nadmiernej szybkości dostępu), będziesz miał losowo nieudane testy.
Podsumowując, nie powiedziałbym, że używanie ApplicationContext
bezpośrednio jest automatycznie niewłaściwe i należy go za wszelką cenę unikać. Jeśli jednak istnieją lepsze opcje (i w większości przypadków), skorzystaj z lepszych opcji.
new MyOtherClass()
obiektu? Wiem o @Autowired, ale kiedykolwiek używałem go tylko na polach, a on się psujenew MyOtherClass()
...