2013-09-22 14 views
30

Spring 3 MVCでフォームがどのように送信されるのか理解できない問題があります。Spring MVC 3でのフォーム提出 - 説明

私がしたいことは、ユーザーの名前を受け取り、彼に表示するコントローラを作成することです。そして、どういうわけか私はそれをやったことがありますが、私はそれがどのように機能するのか本当に理解しませ

@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"; 
    } 
} 

は、私が使用するユーザーへのウェルカムメッセージを表示するには:私もこのようになりますコントローラを持って

<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> 

:..だから

私はこのようになりますフォームを持っていますJSPページの次のコードは次のとおりです。

<c:if test="${not empty person.firstName and not empty person.lastName}"> 
    Hello ${person.firstName} ${person.lastName}! 
</c:if> 

これは動作します(XML構成ファイルは、arこの問題とは無関係です)。

私は、フォームの "modelAttribute"属性が入力値( "path"属性で設定されている)を取り込むべきBean変数を指していると考えました。しかし、外見は、それは非常に異なる方法で動作します。私が行を削除した場合、 "showHelloPage"メソッドから "showHelloPage"メソッドから "Nothing BindingResult nor ..."という例外が出ます。

はまた、最初に、 "のsayHello" 方法はのように見えた:私は意味

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

、それは "ModelAttribute" 注釈を持っていました。私はそれを追加しました。私が読んだチュートリアルでは、常に存在していたからです。しかし、私はそれを削除した後、以前と同じようにすべてがうまくいった。

私の質問はです - "ModelAttribute"アノテーションの使用は何ですか?フォーム内の「modelAttribute」属性を省略する方法はありますか?そして2番目の部分は、フォームを作成する方法(おそらくいくつかの注釈)は、入力の値を適切なBeanのプロパティ(メソッドパラメータとして宣言される)に自動的にバインドしますか?フォームを送信する前に空のBeanを追加する必要はありません(今のように)。

あなたの返信ありがとう(私はすでに読んでいるので、Springのドキュメントへのリンクではありません)。

答えて

38

この場合、@ModelAttribute注釈は、Springがモデル属性として追加する必要のあるオブジェクトを識別するために使用されます。モデル属性は、HttpServletRequest属性の抽象です。基本的には、属性はHttpServletRequestの属性に変換されるいくつかのキーによって識別されるオブジェクトです。これを行うには、Model#addAttribute(String, Object)という属性を手動で追加するか、@ModelAttribute注釈付きの方法を使用するか、または@ModelAttributeというメソッドパラメーターに注釈を付けることによってこれを行うことができます。

Springはハンドラメソッドのパラメータを解決して引数を挿入する方法を理解する必要があります。これは、HandlerMethodArgumentResolverインターフェイスを使用して行います。いくつかの実装クラス(javadocを参照)があり、それぞれがresolveArgument()への責任を持ちます。これは、Springがリフレクションを通してハンドラメソッドinvoke()に使用する引数を返します。SpringはHandlerMethodArgumentResolversupportsParameter()メソッドが特定のパラメータに対してtrueを返す場合にのみ、resolveArgument()メソッドを呼び出します。ここで問題になっている

HandlerMethodArgumentResolver実装は

@ModelAttributeでアノテーション付きメソッドの引数を解決し、@ModelAttributeで注釈が付け方法から の戻り値の処理を述べたModelAttributeMethodProcessorから延びているServletModelAttributeMethodProcessorです。

春(3.2)春は、あなたのハンドラメソッドを呼び出すために必要がある場合registerこのHandlerMethodArgumentResolverなど

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; 
} 

、それはパラメータの型を通って、上記のリストを反復処理し、最初のものを使用しますしますそのsupportsParameter()

ServletModelAttributeMethodProcessorという2つのインスタンスが追加されています(//catch allコメントの後に1つ)。 ModelAttributeMethodProcessorにはannotationNotRequiredフィールドがあり、それは@ModelAttributeを探すべきかどうかを示します。最初のインスタンスは@ModelAttributeを検索する必要があり、2番目のインスタンスは検索しません。 Springは独自のHandlerMethodArgumentResolverインスタンスを登録できるようにこれを行います。// Custom argumentsのコメントを参照してください。この場合


具体的

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

あなたPersonパラメータが注釈されているかどうか、それは問題ではありません。 ModelAttributeMethodProcessorはそれを解決し、フォームフィールドをバインドします。要求パラメータをインスタンスのフィールドに追加します。 ModelAttributeMethodProcessorクラスがそれを処理するので、modelに追加する必要はありません。あなたのshowHelloPage()方法

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

<form>のtaglibで必要とされています。それがそのフィールドのinputを解決する方法です。


だから私の質問はある - "ModelAttribute" anonnatationの使用は何ですか?

モデルに指定されたパラメータ(またはメソッドの戻り値)を自動的に追加します。

フォームの「modelAttribute」属性を省略する方法はありますか?

いいえ、Model内のオブジェクトのためのform結合ルックスとinput要素をHTMLにそのフィールドをバインドします。

第2の部分は、自動的に フォームを作る(メソッドのパラメータとして宣言されます)適切なBeanのプロパティ への入力の値をバインドする方法(おそらくいくつかの注釈)は何ですか?フォームを送信する前に空のBeanを に追加する必要はありません(今のように)。

スプリング<form>タグは、モデル属性オブジェクトにラッチしinputlabel要素を作成するために、そのフィールドを使用します。オブジェクトがモデル内でどのように終わったかは問題ではありません。指定した名前(キー)のモデル属性が見つからない場合は、見たように例外がスローされます。

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

空のBeanを提供する代わりに、自分でhtmlを作成することができます。すべてのSpringの<form>は、Beanのフィールド名を使用してinput要素を作成します。したがって、この

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

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

春のようなものがname属性を使用して、インスタンスフィールドにリクエストパラメータをバインドし作成します。

+0

ご返信いただきありがとうございます、あなたは私に多くの説明をしました。 "ModelAttribute"アノテーションに関するもう1つの質問 - 私が正しく理解している場合、メソッドのパラメータで使用されるこのアノテーションは "model.addAttribute(...)"と同等ですか? –

+0

@MichałTaborメソッドパラメータとして追加してみてください。要求にバインド可能なリクエストパラメータがないため、nullが返される場合は不明です。それ以外の場合、あなたがやっているやり方は正しい方法です。これらは、データ転送オブジェクト(またはSpringフォームのバッキングオブジェクト/コマンドオブジェクト)として知られています。ドキュメントには詳細が必要です。 –

+0

+1、すばらしい説明 – rocketboy

関連する問題