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のドキュメントへのリンクではありません。既に読んでいるので)。
この場合の _@ModelAttribute
_ 注釈は、Springがモデル属性として追加するオブジェクトを識別するために使用されます。モデル属性は、HttpServletRequest
属性からの抽象化です。基本的に、これらはHttpServletRequest
属性への道を見つける何らかのキーによって識別されるオブジェクトです。これを行うには、Model#addAttribute(String, Object)
を使用して属性を手動で追加するか、_@ModelAttribute
_注釈付きメソッドを使用するか、メソッドパラメーターに_@ModelAttribute
_注釈を付けます。
理解する必要があるのは、Springがハンドラーメソッドパラメーターを解決し、引数を注入する方法です。そのために HandlerMethodArgumentResolver
インターフェイスを使用します。実装クラスは多数あり(javadocを参照)、Springがリフレクションを介してハンドラーメソッドをresolveArgument()
するために使用する引数を返すことにより、invoke()
に対する責任があります。 HandlerMethodArgumentResolver
resolveArgument()
メソッドが特定のパラメーターに対して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
属性を使用して、リクエストパラメータをインスタンスフィールドにバインドします。