2013-09-22 14 views
30

Ich habe Probleme zu verstehen, wie ein Formular in Spring 3 MVC Arbeit einreichen.Formular senden im Frühjahr MVC 3 - Erklärung

Was ich tun möchte, ist einen Controller zu erstellen, der den Namen des Benutzers annehmen und ihm anzeigen würde. Und irgendwie habe ich es getan, aber ich verstehe nicht wirklich, wie es funktioniert. Also ..

Ich habe ein Formular, das wie folgt aussieht:

<form:form method="post" modelAttribute="person"> 
    <form:label path="firstName">First name</form:label> 
    <form:input path="firstName" /> 
    <br /> 

    <form:label path="lastName">Last name</form:label> 
    <form:input path="lastName" /> 
    <br /> 

    <input type="submit" value="Submit" /> 
</form:form> 

ich auch einen Controller haben, die wie folgt aussieht:

@Controller 
public class HomeController { 

    @RequestMapping(value = "/", method = RequestMethod.GET) 
    public String showHelloPage(Model model) { 
     model.addAttribute("person", new Person()); 
     return "home"; 
    } 

    @RequestMapping(value = "/", method = RequestMethod.POST) 
    public String sayHello(Person person, Model model) { 
     model.addAttribute("person", person); 
     return "home"; 
    } 
} 

Um eine Begrüßungsnachricht ich an einen Benutzer anzuzeigen verwenden der folgende Code in der JSP-Seite:

Und es funktioniert (Ich unterlasse die XML-Konfigurationsdateien, weil sie ar irrelevant für das Problem).

Ich dachte, dass das Attribut "modelAttribute" in einem Formular auf die Bean-Variable zeigt, die mit den Werten der Eingaben (wie in ihren "Pfad" -Attributen festgelegt) gefüllt werden sollte. Aber sieht, es funktioniert auf eine ganz andere Art und Weise. Wenn ich die Linie

model.addAttribute("person", new Person()); 

von „showHelloPage“ Methode entfernen erhalte ich eine (gemeinsame) Ausnahme „Weder BindingResult noch ...“.

Auch am Anfang der "sayHello" Methode sah aus wie:

(...) 
public String sayHello(@ModelAttribute("person") Person person, Model model) { 
(...) 

Ich meine, es die "ModelAttribute" Anmerkung hatte. Ich habe es hinzugefügt, weil es in den Tutorials, die ich gelesen habe, immer präsent war. Aber nachdem ich es entfernt habe, hat alles wie vorher gut funktioniert.

Also meine Frage ist - was ist die Verwendung der "ModelAttribute" Anonnation? Ist es eine Möglichkeit, ein "ModelAttribute" -Attribut in einem Formular wegzulassen? Und der zweite Teil, was ist der Weg (vielleicht eine Annotation), um ein Formular zu machen, bindet die Werte der Eingaben automatisch an die Eigenschaften der richtigen Bean (die als Methodenparameter deklariert werden würden)? Ohne eine leere Bean vor dem Senden eines Formulars hinzufügen zu müssen (wie ich es jetzt tun muss).

Vielen Dank für Ihre Antworten (die keine Links zu der Frühjahrsdokumentation sind, weil ich sie bereits gelesen habe).

Antwort

38

Die Annotation @ModelAttribute in diesem Fall wird verwendet, um ein Objekt zu identifizieren, das Spring als Modellattribut hinzufügen sollte. Modellattribute sind eine Abstraktion von den Attributen HttpServletRequest. Im Grunde genommen handelt es sich um Objekte, die durch einen Schlüssel identifiziert werden, der seinen Weg in die HttpServletRequest Attribute findet. Sie können dies tun, indem Sie manuell ein Attribut mit Model#addAttribute(String, Object) hinzufügen, eine annotierte Methode @ModelAttribute haben oder einen Methodenparameter mit @ModelAttribute annotieren.

Die Sache, die Sie verstehen müssen, ist, wie Spring Ihre Handler-Methode Parameter löst und Argumente eingibt. Dazu wird die Schnittstelle HandlerMethodArgumentResolver verwendet. Es gibt eine Reihe von Implementierungsklassen (siehe Javadoc), und jeder trägt die Verantwortung für resolveArgument(), indem er das Argument, das Spring verwendet, an invoke() Ihre Handler-Methode durch Reflektion zurückgibt.Spring ruft nur die Methode resolveArgument() auf, wenn die Methode HandlerMethodArgumentResolversupportsParameter() den spezifischen Parameter true zurückgibt.

Die HandlerMethodArgumentResolver Umsetzung in Frage hier ist ServletModelAttributeMethodProcessor die von ModelAttributeMethodProcessor erstreckt, die mit @ModelAttribute kommentierten

Resolves Methodenargumente erklärt und behandelt Rückgabewerte von Methoden mit @ModelAttribute kommentiert.

Spring (3.2) wird register diese HandlerMethodArgumentResolver und andere

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { 
     List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); 

    // Annotation-based argument resolution 
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); 
    resolvers.add(new RequestParamMapMethodArgumentResolver()); 
    resolvers.add(new PathVariableMethodArgumentResolver()); 
    resolvers.add(new ServletModelAttributeMethodProcessor(false)); 
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters())); 
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters())); 
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); 
    resolvers.add(new RequestHeaderMapMethodArgumentResolver()); 
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); 
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); 

    // Type-based argument resolution 
    resolvers.add(new ServletRequestMethodArgumentResolver()); 
    resolvers.add(new ServletResponseMethodArgumentResolver()); 
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters())); 
    resolvers.add(new RedirectAttributesMethodArgumentResolver()); 
    resolvers.add(new ModelMethodProcessor()); 
    resolvers.add(new MapMethodProcessor()); 
    resolvers.add(new ErrorsMethodArgumentResolver()); 
    resolvers.add(new SessionStatusMethodArgumentResolver()); 
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); 

    // Custom arguments 
    if (getCustomArgumentResolvers() != null) { 
     resolvers.addAll(getCustomArgumentResolvers()); 
    } 

    // Catch-all 
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); 
    resolvers.add(new ServletModelAttributeMethodProcessor(true)); 

    return resolvers; 
} 

Wenn der Frühling braucht Ihre Handler-Methode aufzurufen, ist es durch die Parametertypen und durch die oben aufgeführte Liste laufen werden und verwenden Sie die erste dass supportsParameter().

Beachten Sie, dass zwei Instanzen von ServletModelAttributeMethodProcessor hinzugefügt werden (einer nach einem //catch all Kommentar). Die ModelAttributeMethodProcessor hat ein annotationNotRequired Feld, das sagt, ob es nach der @ModelAttribute oder nicht suchen sollte. Die erste Instanz muss nach @ModelAttribute suchen, die zweite nicht. Spring tut dies, so dass Sie Ihre eigenen HandlerMethodArgumentResolver Instanzen registrieren können, siehe // Custom arguments Kommentar.


Speziell

@RequestMapping(value = "/", method = RequestMethod.POST) 
public String sayHello(Person person, Model model) { 
    model.addAttribute("person", person); 
    return "home"; 
} 

In diesem Fall spielt es keine Rolle, ob Ihre Person Parameter oder nicht kommentiert wird. A ModelAttributeMethodProcessor löst es auf und bindet Formularfelder, dh. Anforderungsparameter an die Felder der Instanz. Sie sollten es nicht einmal zur model hinzufügen müssen, da die ModelAttributeMethodProcessor Klasse das handhaben wird.

In Ihrer showHelloPage() Methode

model.addAttribute("person", new Person()); 

mit dem <form> taglib benötigt. So löst es seine input Felder auf.


Also meine Frage ist - was ist die Verwendung des "ModelAttribute" anonnatation?

Zum automatischen Hinzufügen des angegebenen Parameters (oder Methodenrückgabewerts) zum Modell.

Ist es eine Möglichkeit, ein "ModelAttribute" -Attribut in einem Formular wegzulassen?

Nein, die form Bindung sieht für ein Objekt in der Model und bindet seine Felder input Elemente in HTML.

Und der zweite Teil, was ist die Art und Weise (vielleicht einige Anmerkung) eine Form automatisch binden Eingänge Werte der richtigen Bohne Eigenschaften (die als Methodenparameter deklariert werden würde) zu machen? Ohne eine leere Bean vor dem Senden eines Formulars hinzufügen (wie ich es jetzt tun muss).

A Feder <form> tag klinkt ein Modellobjekt-Attribut und verwendet seine Felder input und label Elemente zu schaffen. Es spielt keine Rolle, wie das Objekt so lange im Modell landete. Wenn es kein Modellattribut mit dem von Ihnen angegebenen Namen (Schlüssel) findet, werden Ausnahmen ausgelöst, wie Sie gesehen haben.

<form:form method="post" modelAttribute="person"> 

Die Alternative zur Bereitstellung einer leeren Bean besteht darin, den HTML-Code selbst zu erstellen. Alle <form> von Spring verwendet die Feldnamen der Bean, um ein input-Element zu erstellen. Also das

<form:form method="post" modelAttribute="person"> 
    <form:label path="firstName">First name</form:label> 
    <form:input path="firstName" /> 

schafft so etwas wie

<form method="post" action="[some action url]"> 
    <label for="firstName">First name<label> 
    <input type="text" name="firstName" value="[whatever value firstName field had]" /> 
    ... 

Frühling bindet Anforderungsparameter auf Instanzfelder mit dem Attribut name.

+0

Vielen Dank für Ihre Antwort, Sie haben mir eine Menge erklärt. Nur noch eine Frage zu der Annotation "ModelAttribute" - wenn ich Sie richtig verstehe, ist diese Annotation, die mit einem Methodenparameter verwendet wird, äquivalent zu "model.addAttribute (...)"? –

+0

@ MichałTabor Versuchen Sie, es als Methodenparameter hinzuzufügen. Ich bin mir nicht sicher, ob, weil die Anfrage keine Anfrageparameter hat, die gebunden werden können, sie 'null' zurückgibt. Ansonsten ist die Art, wie Sie vorgehen, der richtige Weg. Diese werden als Datenübertragungsobjekte (oder Spring form backing objects/command objects) bezeichnet. Die Dokumentation sollte mehr Details enthalten. –

+0

+1, große Erklärung – rocketboy