web-dev-qa-db-ja.com

Spring MVC 3でのフォーム送信-説明

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」メソッドから、(一般的な)例外「BindResultも...もありません」を受け取ります。

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

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

つまり、「ModelAttribute」注釈がありました。私が読んだチュートリアルでは常に存在していたため、追加しました。しかし、それを削除した後は、以前と同様にすべてがうまく機能しました。

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

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

31
Michał Tabor

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

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

ここで問題のHandlerMethodArgumentResolver実装は ServletModelAttributeMethodProcessor から拡張されます ModelAttributeMethodProcessor which

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

Spring(3.2)は register this 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がハンドラーメソッドを呼び出す必要がある場合、パラメータータイプと上記のリストを繰り返し処理し、最初のsupportsParameter()を使用します。

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


具体的には

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

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

showHelloPage()メソッドで

_model.addAttribute("person", new Person());
_

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


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

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

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

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

そして、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]" />
    ...
_

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

39