31

Spring 3 MVC でフォーム送信がどのように機能するかを理解するのに問題があります。

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

次のようなフォームがあります。

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

次のようなコントローラーもあります。

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

ユーザーにウェルカム メッセージを表示するには、JSP ページで次のコードを使用します。

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

そして、それは機能します (XML 構成ファイルは問題とは関係がないため省略します)。

フォームの「modelAttribute」属性は、入力の値を入力する必要があるBean変数を指していると思いました(「パス」属性で設定)。しかし、見た目は、非常に異なる方法で機能します。行を削除すると

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

「showHelloPage」メソッドから、(一般的な)例外「BindingResultでも...でもない」が発生します。

また、最初は、「sayHello」メソッドは次のように見えました。

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

つまり、「ModelAttribute」アノテーションがありました。私が読んだチュートリアルでは常に存在していたので、追加しました。しかし、それを削除した後、以前と同じようにすべてがうまくいきました.

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

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

4

1 に答える 1

40

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

理解する必要があるのは、Spring がハンドラー メソッドのパラメーターを解決し、引数を挿入する方法です。HandlerMethodArgumentResolverそのためにインターフェイスを使用します。多数の実装クラスがあり (javadoc を参照)、 Spring が使用する引数をリフレクションを通じてハンドラー メソッドresolveArgument()に返す責任があります。invoke()Spring は、メソッドが特定のパラメーターに対して返されたresolveArgument()場合にのみメソッドを呼び出します。HandlerMethodArgumentResolver supportsParameter()true

ここHandlerMethodArgumentResolverで問題となっている実装は、どの状態ServletModelAttributeMethodProcessorから拡張されているかです。ModelAttributeMethodProcessor

@ModelAttribute アノテーションが付けられたメソッド引数を解決し、@ModelAttribute アノテーションが付けられたメソッドからの戻り値を処理します。

Spring (3.2) はこれと他のものを登録します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;
}

Spring がハンドラー メソッドを呼び出す必要がある場合、Spring はパラメーターの型と上記のリストを反復処理し、supportsParameter().

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


具体的には

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

この場合、Personパラメーターに注釈が付けられているかどうかは問題ではありません。AModelAttributeMethodProcessorはそれを解決し、フォーム フィールドをバインドします。リクエスト パラメータをインスタンスのフィールドに追加します。クラスがそれを処理するmodelため、に追加する必要さえありません。ModelAttributeMethodProcessor

あなたのshowHelloPage()方法では

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

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


だから私の質問は - 「ModelAttribute」注釈の使用は何ですか?

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

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

いいえ、formバインディングは 内のオブジェクトを探し、Modelそのフィールドを htmlinput要素にバインドします。

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

Spring<form>タグはモデル属性オブジェクトにラッチし、そのフィールドを使用して要素を作成inputします。labelオブジェクトがモデルにどのように組み込まれたかは問題ではありません。指定した名前 (キー) を持つモデル属性が見つからない場合は、前述のように例外がスローされます。

 <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]" />
    ...

nameSpring は、属性を使用してリクエスト パラメータをインスタンス フィールドにバインドします。

于 2013-09-22T14:12:41.827 に答える