Einer der Gründe ist die Testbarkeit. Angenommen, Sie haben diese Klasse:
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"));
}
}
Wie können Sie diese Bohne testen? ZB so:
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);
}
}
Einfach richtig?
Während Sie (aufgrund der Anmerkungen) immer noch von Spring abhängig sind, können Sie Ihre Abhängigkeit von Spring entfernen, ohne Code zu ändern (nur die Anmerkungsdefinitionen), und der Testentwickler muss nichts darüber wissen, wie Spring funktioniert (vielleicht sollte er es trotzdem, aber Es ermöglicht die Überprüfung und Prüfung des Codes getrennt von den Funktionen der Feder.
Bei Verwendung des ApplicationContext ist dies weiterhin möglich. Dann müssen Sie sich jedoch lustig machen, ApplicationContextwas eine riesige Schnittstelle ist. Sie benötigen entweder eine Dummy-Implementierung oder Sie können ein Verspottungsframework wie Mockito verwenden:
@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);
}
}
Dies ist eine ziemliche Möglichkeit, aber ich denke, die meisten Leute würden zustimmen, dass die erste Option eleganter ist und den Test einfacher macht.
Die einzige Option, die wirklich ein Problem darstellt, ist diese:
@Component
class MyBean {
@Autowired
MyBean(StringOutput out) {
out.print(new HttpLoader().load("http://stackoverflow.com"));
}
}
Das Testen erfordert große Anstrengungen, oder Ihre Bean wird bei jedem Test versuchen, eine Verbindung zum Stackoverflow herzustellen. Und sobald Sie einen Netzwerkfehler haben (oder die Administratoren bei Stackoverflow Sie aufgrund einer übermäßigen Zugriffsrate blockieren), werden Sie zufällig fehlgeschlagene Tests haben.
Abschließend würde ich nicht sagen, dass die ApplicationContextdirekte Verwendung automatisch falsch ist und unter allen Umständen vermieden werden sollte. Wenn es jedoch bessere Optionen gibt (und in den meisten Fällen auch), verwenden Sie die besseren Optionen.
new MyOtherClass()? Ich weiß über @Autowired Bescheid, aber ich habe es immer nur auf Feldern verwendet und es bricht weiternew MyOtherClass().