In der aktuell täglichen Arbeit mit Spring 2.5 fasziniert einerseits die Leichtigkeit der Anwendungen mit Spring MVC, andererseits treten ab und zu Kuriositäten auf. Drei davon werden hier namentlich mit Vorschlägen zur Behandlung genannt.
Die automatische Erzeugung von IDs bei Form-Elementen durch Spring bringt zwei Kuriositäten zutage.
Wird ein Formobjekt erzeugt und via Modelattribut an die View zur Darstellung übergeben, so werden die IDs der einzelnen Formelemente durch Spring automatisch generiert. Das ist sinnvoll, da diese IDs nicht kryptisch generiert werden, sondern den einzelnen Attributen des Formobjektes entsprechen. So kann ein Form-Input-Element z.B. mit jQuery ganz einfach angesprochen werden. Bis jetzt ist das alles prima. Wie Spring jedoch reagiert, wenn Checkboxen als Formelemente genutzt werden, widerspricht der Erwartung. Bei Checkboxen beginnt Spring nun doch, die IDs kryptisch zu generieren, so dass eine einzelne Checkbox nicht nur den Namen des Attributes des Formobjektes bekommt, sondern zusätzlich einen Index.
Aus dem Form-Tag:
<td colspan="2"> <form:checkbox path="subscriber"/>Newsletter </td>
Wird im Browser:
<td colspan="2"> <input id="subscriber1" name="subscriber" type="checkbox" value="true"/> <input type="hidden" name="_subscriber" value="on"/>Newsletter </td>
Das ergibt auf den ersten Blick keinen Sinn und zwingt den Entwickler erst einmal zur Fehlersuche, was ja bei JavaScript und JQuery nur bedingt Spaß macht.
Die zweite Abnormalität tritt bei der Generierung der IDs von Objektattributen auf. Gibt es beispielsweise im Formobjekt ein Attribut vom Typ Kunde, dann erzeugt Spring bei der automatischen Generierung von IDs Folgendes daraus.
Aus dem Form-Tag:
<td> <form:input path="customer.firstName" /> </td>
Wird im Browser:
<td> <input id="customer.firstName" name="customer.firstName" type="text" value=""/> </td>
An sich ja ganz logisch, doch wie wird mit jQuery auf dieses Input-Element zugegriffen? Da hier der Punkt decodiert werden muss, macht das Ganze wieder weniger Spaß.
Der Lösungsansatz an dieser Stelle ist ganz trivial. Die IDs einfach selbst setzen oder das kuriose Verhalten einfach akzeptieren und die generierten IDs nutzen.
Auch die Parameter im Controller bringen ihre Hürden mit sich.
Die Einfachheit des Controllers könnte wie folgt definiert werden: Gib es der Methode als Parameter und wenn es im Model existiert, kann darauf zugegriffen werden.
Jeder, der sich ein wenig mit Spring beschäftigt und es in der Praxis zum Einsatz bringt, ist von der unkomplizierten Methodendefinition im MVC Controller von Spring begeistert. Werden z.B. das Model und der Request benötigt, so werden diese einfach als Parameter der Methode übergegeben und es kann auf sie im Methodenrumpf zugegriffen werden. Super Lösung, warum nicht immer so einfach? Leider gibt es auch hier Ausnahmefälle, in denen die Reihenfolge der Parameter von Bedeutung ist.
Ein solcher Fall tritt ein, wenn die Daten einer Form durch einen Spring Validator validiert werden sollen. Wird die Reihenfolge der Parameter nicht beachtet, erscheint folgende Fehlermeldung:
SCHWERWIEGEND: Servlet.service() for servlet springFormKurios threw exception
java.lang.IllegalStateException: Errors/BindingResult argument declared without preceding model attribute. Check your handler method signature!
Um dieses Verhalten zu vermeiden, ist folgende ungeschriebene Regel zu beachten: Das BindingResult muss bei den Parametern im Methodenkopf immer nach dem Formobjekt stehen. So kommt auch Spring damit klar und ermöglicht eine problemlose Arbeit mit dem BindingResult.
@RequestMapping(value = "/yeahYeah", method = RequestMethod.POST) public String processSubmit( @ModelAttribute("form") SimpleForm simpleForm, BindingResult result) { validator.validate(simpleForm, result); if (result.hasErrors()) { return "form"; } return "success"; }
Wer ein Beispiel zum Spielen braucht, kann folgende kleine Beispielanwendung runterladen: SpringFormKurios.war