Nahezu alle campusboard-Projekte nutzen das Spring Framework (insbesondere Spring Web MVC).
Sinnvoll ist zur Einarbeitung immer ein Blick in die entsprechenden Tutorials, die es zahlreich im Netz gibt:
Das Grundprinzip sei im Folgenden dennoch noch einmal kurz zusammengefasst.
Der allgemeine Ablauf sieht folgendermaßen aus:
Benutzer ruft im Browser auf:
http://localhost:8080/projektname/.../tuWas.do
Der Server schaut daraufhin in der WEB-INF/web.xml nach, was mit dieser URL zu tun ist.
Bei Spring-Projekten finden sich dort üblicherweise Einträge dieser Art:
<servlet-mapping>
<servlet-name>controller</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
und
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Dies bedeutet, dass alle Aufrufe mit der Endung .do an das Spring-Servlet weitergeleitet werden.
In der Regel werden die Controller-Klassen mithilfe des Annotation-basierten Programmieransatzes geschrieben.
Wo sich die Controller-Klassen befinden muss Spring in der Konfigurationsdatei „spring-servlet.xml“ mitgeteilt werden. Dies sieht z.B. folgendermaßen aus:
<context:component-scan base-package="de.fhzw.portal.xyz.controller" />
Durch die Annotation @Controller wird Spring mitgeteilt, dass diese Klasse einen Controller implementiert.
Die bei Struts automatisch in der Session gespeicherten Form-Beans müssen hier explizit mit der Annotation @SessionAttributes gekennzeichnet werden. Ansonsten werden diese bei jedem Aufruf neu gespeichert und initialisiert.
Eine zum Aufruf aus dem Beispiel passende Controller-Klasse könnte z.B. folgendermaßen aussehen:
package de.fhzw.portal.xyz.controller;
@Controller
@SessionAttributes({ "DemoForm" })
@RequestMapping("/tuWas.do")
public class TuWasController {
...
}
Der Code sollte immer so aufgebaut sein, dass er möglichst gut wartbar und intuitiv erfassbar ist.
Prinzipiell können URL, Controller-Klasse und ausgeführte Methode beliebig benannt werden.
Die Namen sollten jedoch einer gewissen Systematik folgen, dammit aus dem Aufruf möglichst direkt erkennbar ist, an welcher Stelle dieser verarbeitet wird.
Im Detail bedeutet dies: Pro URL sollte eine Controller-Klasse vorgesehen werden und deren Namen sollte aus der URL ersichtlich sein.
Innerhalb der Klasse erfolgt die Unterscheidung welche Methode ausgeführt werden soll einheitlich anhand des immer gleichen Parameters. Üblicherweise wird dieser "action" benannt.
Der Methodenname sollte unbedingt immer dem Wert von "action" entsprechen. Dadurch ist immer schnell ersichtlich, welcher Aufruf durch welche Methode implementiert wird.
Beispiel:
@RequestMapping(params = "action=save")
public String save(@ModelAttribute("DemoForm") DemoForm form) {
log.info("===== save ENTER form={}", form);
...
}
In der Controller-Methode wird am Ende ein String zurückgegeben, der den Namen der aufzurufenden View trägt.
Beispiel:
In dem Fall endet die Controller-Methode mit:
return "Darstellung"; // um die Darstellung.jsp Seite im Anschluss aufzurufen
Dies hat dann zur Folge, dass die JSP /Darstellung.jsp als Ausgabe erscheint.
Wenn der Aufruf der Controller-Klasse aus einem Formular erfolgte, dann kann man Spring JavaBeans benutzen, um Eingabewerte zu verwalten.
Damit dies geschieht, muss in der JSP ein form:form-Tag verwendet werden.
Beispiel:
<form:form action="/tuWas.do" modelAttribute="DemoForm">
Id: <form:input path="id" />
Text: <form:input path="name" />
</form:form>
Der hinter "modelAttribute" angegebene Name gehört zu einer Bean, in welcher die Daten für die Verarbeitung des Formulars gespeichert werden. Damit das Beispiel oben funktioniert, muss die Bean "DemoForm" mindestens über die beiden Attrigute "id" und "name" verfügen und es müssen passende getter- und setter-Funktionen - hier also getId()/setId(...) und getName()/setName(...) vorhanden sein.
Um sich Arbeit zu sparen, wird diese Bean in die Controller-Klasse injiziert.Dies geschieht durch die Angabe der Annotation
@ModelAttribute bei den Methoden, die mit den Bean-Daten arbeiten sollen. Dies sind in der Regel mindestens die Methode, die die Daten für die Bearbeitung in die Bean lädt und die Methode, die nach dem Drücken des Submit-Buttons diese verarbeitet.
Beispiel:
@RequestMapping(params = { "action=edit" }) // lädt die Daten für die Bearbeitung
public String edit(@ModelAttribute("DemoForm") DemoForm form) { ... }
@RequestMapping(params = { "action=save" }) // erhält die bearbeiteten Daten zum Speichern
public String save(@ModelAttribute("DemoForm") DemoForm form) { ... }
Da die Formulardaten bei Fehlermeldungen im Rahmen einer Validierung nicht verloren gehen sollen, werden diese in der Regel in der Session gespeichert.
Die Annotation @SessionAttributes sorgt dafür. Ist diese angegeben, so werden die Daten nicht bei jedem Methodenaufruf neu erzeugt, sondern in der Session gespeichert. Über die Annotation @ModelAttribute über einer Methode, die eine Instanz der SessionBean zurück gibt kann die Erzeugung komplett unter eigener Kontrolle erfolgen. Beispiel:
@Controller
@SessionAttributes({ "DemoForm" })
@RequestMapping("/tuWas.do")
public class TuWasController {
@ModelAttribute("DemoForm")
public createDemoForm() {
log.info("----- createDemoForm -----"); // nur um zu sehen, wann die Bean erzeugt wurde
return new DemoForm();
}
...
}
Achtung: Das aktuell eingesetzte UPortal ruft beim Tabwechsel die letzte Url des Tabs ohne Parameter auf. Falls keine Methode existiert, die ohne Parameter aufgerufen werden kann, führt das zu einem Fehler, an der Oberfläche den Anwendern mit der folgender Meldung angezeigt: "This channel failed to render. Reboot the channel."
Daher sollte in jedem Controller immer eine Default-Methode vorhanden sein. Beispiel:
@RequestMapping(method = RequestMethod.GET) // wird immer angezeigt, wenn keine Parameter angegeben sind
public String default_method(@ModelAttribute("DemoForm") DemoForm form) {
return overview(form);
}