Convention over Configuration?
Zu einem meiner letzten Blog-Posts gab es einen recht interessanten Kommentar, der mehr Support für Convention over Configuration und Configuration by Exception in Spring anmahnte.
Vorab: Convention over Configuration bedeutet, dass man sich auf Konventionen verlässt, die zu Einstellungen führen, die in den meisten Fällen ausreichend sind. Nur in Ausnahmefällen nutzt man Konfiguration, um Dinge dann doch anders einzustellen. Configuration by Exception bedeutet im wesentichen dasselbe. Diese Ansätze sind in Systemen wie Ruby on Rails oder Grails weit verbreitet und führen zu sehr kompakten Code.
Zunächst: Spring kann in zahlreichen Bereichen mit solchen Möglichkeiten aufwarten.
Konkret ging es im kritisierten Beispiel um das Einlesen von Properties. Man kann hier neben Expression Language auch auf die "klassischen" Methoden zugreifen. Und zwar konkret auf den
PropertyOverrideConfigurer, der einfach Properties nimmt und dann Werte von Spring-Bean überschreibt. Dazu muss man in die XML-Konfiguration
<context:property-override location="props.properties" /> einfügen. Wenn in der Datei
props.properties ein Eintrag wie
hallo.name=Ein Name existiert, wird bei der Spring-Bean
hallo die Methode
setName() mit dem Parameter
"Ein Name" aufgerufen. Man kann also ein Default im Code oder in der Spring-Konfiguration haben, und diesen anschließend überschreiben.
Ein anderes Beispiel für Convention over Configuration ist der folgende Web-Controller:
@Controller
public class KundeController {
@RequestMapping
public Kunde showForm() {
return new Kunde("Wolff","Eberhard",42);
}
}
Er wird bei der URL
http://localhost:8080/mywebapp/myservlet/kunde aktiviert, weil er
KundeController heißt. Als View-Name wird kunde genutzt, weil es der letze Teil der URL ist. Und das zurückgegeben Objekt steht untern dem Namen kunde in der View zur Verfügung, weil der Name der Klasse Kunde ist. Wie man sieht, ergibt sich so sehr kompakter Code.
Und schließlich gibt es zahlreiche Möglichkeiten, Autowiring zu nutzen. Zum Beispiel in XML:
<beans default-autowire="byType"
...>
<bean id="orderService" class="....OrderServiceImpl" />
...
oder im Code:
@Service
public class OrderServiceImpl implements OrderService {
private CustomerRepository customerRepository;
@Autowired
public void setCustomerRepository(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
...
}
In beiden Fällen werden typ-kompatible Spring-Beans an diesen Stellen injiziert, so dass man das Wiring der Anwendung nicht weiter definieren muss. Auch das macht den Code wesentlich kompakter.
Das interessante ist nun, dass diese Möglichkeiten existieren, aber durchaus kritisch betrachtet werden:
- Der PropertyOverrideConfigurer erlaubt es, bei beliebigen Spring-Beans beliebige Werte zu überschreiben. Das ist im allgemeinen zu mächtig. Man will meistens kontrollieren, welche Werte wo gesetzt werden können und auch sehen, womit die Werte überschrieben werden. Daher habe ich bisher in der Praxis eigentlich nur den PropertyPlaceholderConfigurer gesehen, der aber wesentlich expliziter und aufwändiger ist.
- Bei den Controllern kommt in Trainings oder Präsentationen oft die Frage, ob man nicht das Mapping zwischen URLs und Code irgendwo zentral im XML konfigurieren kann. Und das, obwohl die Mappings mittlerweile auch von den Tools gut unterstützt werden und man dadurch Übersichtlichkeit hat. Der Aufwand für eine explizite Angabe der URL, des View-Names und der Model-Namens sind auch nicht sehr groß:
@Controller
public class KundeController {
@RequestMapping("/kunde")
public Kunde showForm() {
return new ModelAndView("kunde","kunde",new Kunde("Wolff","Eberhard",42));
}
- Und Autowiring wird auch selten genutzt - das häufigste Gegenargument ist die mangelnde Übersicht, was genau wohin injiziert wird. Das ist vor allem spannend, weil in den meisten Fällen sowieso nur ein typ-kompatible Spring-Bean existiert, weil es eben nur eine Implementierung des Interfaces oder eine Instanz der Klasse gibt. Nur in Ausnahmefällen gibt es eine echte Wahl. Das bedeutet, dass die explizite Konfiguration nicht wesentliche Vorteile bringt - weil in den meisten Fällen sowieso klar ist, was wohin injiziert werden muss.
Was bedeutet das nun? Spring hat Möglichkeiten für Convention over Configuration. Mein Eindruck ist allerdings, dass diese Features zwar bekannt sind, aber in der Praxis wenig genutzt werden, weil man lieber explizit konfiguriert und einstellt, statt sich auf "Magie" zu verlassen. Das bedeutetet, dass Spring-Entwickler entweder andere Vorlieben oder andere Erfahrungen als beispielsweise Ruby-on-Rails-Entwickler haben - weil sie der "Magie" mistrauen oder schon gesehen haben, dass Systeme mit mehr "Magie" schwerer zu verstehen oder zu warten sind. Vielleicht müssen Ruby-on-Rails-Entwickler das noch lernen - oder vielleicht müssen Spring-Entwickler mehr Vertrauen zu der "Magie" entwickeln.
Labels: Convention over Configuration, Spring