コントローラのメソッドの引数列に@ModelAttributeアノテーションが付けたら何があった

ソースコードは[サンプルアプリケーション](https://github.com/saladlam/spring-noticeboard)からとった。

Thymeleafテンプレートの入力フォーム'resources/templates/private/message.html'

```html
<!DOCTYPE html>
<html th:lang="${#locale.language}" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
  <title>[[#{applicationName}]] - [[${isEdit}? #{editMessage}: #{newMessage}]]</title>
  <meta charset="utf-8">
  <link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css">
</head>
<body>
<div class="ui container">
  <h4 th:text="${isEdit}? #{editMessage}: #{newMessage}"></h4>
  <form th:action="@{/manage/__${postHandler}__}" method="post" th:object="${message}" class="ui form">
    <fieldset>
      <legend>[[#{message}]]</legend>
      <div class="field">
        <label>[[#{message.publishDate}]]</label>
        <input type="text" th:field="*{publishDate}" />
      </div>
      <div class="field">
        <label>[[#{message.removeDate}]]</label>
        <input type="text" th:field="*{removeDate}" />
      </div>
      <div class="field">
        <label>[[#{message.description}]]</label>
        <textarea name="description" th:text="*{description}"></textarea>
      </div>
      <button class="ui mini primary button">[[#{save}]] <i class="send icon"></i></button>
    </fieldset>
  </form>
</div>
</body>
</html>
```

ブラウザが[http://localhost:8080/manage/new](http://localhost:8080/manage/new)にアクセスし、下のHTMLコードがもらえる

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Notice board - New message</title>
  <meta charset="utf-8">
  <link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css">
</head>
<body>
<div class="ui container">
  <h4>New message</h4>
  <form action="/manage/new/save" method="post" class="ui form"><input type="hidden" name="_csrf" value="87baeee6-deea-4c4b-8b2b-b17e9be876e0"/>
    <fieldset>
      <legend>Message</legend>
      <div class="field">
        <label>Publish Date</label>
        <input type="text" id="publishDate" name="publishDate" value="" />
      </div>
      <div class="field">
        <label>Remove Date</label>
        <input type="text" id="removeDate" name="removeDate" value="" />
      </div>
      <div class="field">
        <label>Message</label>
        <textarea name="description"></textarea>
      </div>
      <button class="ui mini primary button">Save <i class="send icon"></i></button>
    </fieldset>
  </form>
</div>
</body>
</html>
```

下のように資料を入れ

![form_filled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232557/b5be8174-5a80-fe5d-31af-ada50503d339.png)

'Save'をクリックし、HTTP POSTリクエストと下のパラメータが[http://localhost:8080/manage/new/save](http://localhost:8080/manage/new/save)に送る

![post.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232557/08bb8b17-aa22-6c4b-911d-eaca54c22f11.png)

クラス*info.saladlam.example.spring.noticeboard.controller.PrivateController*のメソッド**saveCreateMessage()**がリクエストを処理する

```java
@Controller
@RequestMapping("/manage")
public class PrivateController {

	@PostMapping("/new/save")
	public String saveCreateMessage(@ModelAttribute MessageDto message, BindingResult errors) {
		message.setOwner(this.getLoginName());
		this.messageService.save(message);
		return "redirect:/manage";
	}
	// ...
}
```

インスタンス*MessageDto*はクラス*org.springframework.web.method.annotation.ModelAttributeMethodProcessor*が建てる。実際建てる手順のはメソッド**resolveArgument()**に定義される

```java
	@Override
	@Nullable
	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
		Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

		String name = ModelFactory.getNameForParameter(parameter);
		ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
		if (ann != null) {
			mavContainer.setBinding(name, ann.binding());
		}

		Object attribute = null;
		BindingResult bindingResult = null;

		if (mavContainer.containsAttribute(name)) {
			attribute = mavContainer.getModel().get(name);
		}
		else {
			// Create attribute instance
			try {
				attribute = createAttribute(name, parameter, binderFactory, webRequest);
			}
			catch (BindException ex) {
				if (isBindExceptionRequired(parameter)) {
					// No BindingResult parameter -> fail with BindException
					throw ex;
				}
				// Otherwise, expose null/empty value and associated BindingResult
				if (parameter.getParameterType() == Optional.class) {
					attribute = Optional.empty();
				}
				bindingResult = ex.getBindingResult();
			}
		}

		if (bindingResult == null) {
			// Bean property binding and validation;
			// skipped in case of binding failure on construction.
			WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
			if (binder.getTarget() != null) {
				if (!mavContainer.isBindingDisabled(name)) {
					bindRequestParameters(binder, webRequest);
				}
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new BindException(binder.getBindingResult());
				}
			}
			// Value type adaptation, also covering java.util.Optional
			if (!parameter.getParameterType().isInstance(attribute)) {
				attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
			}
			bindingResult = binder.getBindingResult();
		}

		// Add resolved attribute and BindingResult at the end of the model
		Map<String, Object> bindingResultModel = bindingResult.getModel();
		mavContainer.removeAttributes(bindingResultModel);
		mavContainer.addAllAttributes(bindingResultModel);

		return attribute;
	}
```

ここに*MessageDto*インスタンスがもらえる

```java
bindRequestParameters(binder, webRequest);
```

そしてもしValidatorが定義されたら、ここに実行する

```java
validateIfApplicable(binder, parameter);
```

最後に*MessageDto*インスタンスは引数**message**としてコントローラに渡す。