Springで画面の入力値を受け取る


超基本的な内容です(自分の備忘録)。
画面のあるWebアプリを開発していると、画面での入力値をPOSTしてサーバ側で受け取りその値を新規登録・更新・削除、、、なんてのがよくあると思います。本記事は、SpringではPOSTした値をサーバ側で受け取る時にどのような実装になるのか、というお話です。
ちなみにSAStrutsなんかだと、その部分の実装は以下のような感じです。

```
@ActionForm
@Resource
protected AddForm addForm;

@Execute
public String add() {
	// 何らかの処理
	String id = addForm.id;
	// 何らかの処理
	return "list.jsp?redirect=true";
}
```

@ActionFormアノテーション、@Resourceアノテーションをつけるだけで、自動的にPOSTされた値がaddFormオブジェクトの各フィールドにマッピングされ、対象のExecuteメソッドの中で利用出来るようになります。

ここでは、画面上に苗字(firstName)、名前(lastName)の2つのテキストボックスがあるものとして、これらをPOSTしてみる、、というのを例にまとめてみます。

# Formクラスの作成
まずは、画面上での入力値を保持するFormクラスの作成が必要です(このあたりは他のFWとかも一緒ですね)。ただSAStrutsしかやったことのない人、注意が必要です、このFormクラスはsetter/getterが必要になってきます。

```CustomerForm.java
package com.example.form;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class CustomerForm {

	@NotNull
	@Size(min = 1, max = 127)
	private String firstName;

	@NotNull
	@Size(min = 1, max = 127)
	private String lastName;

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
}
```

ちなみに、setter/getterを作ってあげないと、以下みたいな感じで実行時に怒られます。

```
org.springframework.beans.NotReadablePropertyException: Invalid property 'lastName' of bean class [com.example.form.CustomerForm]: Bean property 'lastName' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
```

# ControllerクラスでFormを受け取る
画面上での入力値を保持するFormクラスをControllerクラスで受け取ってみます。

```CustomerController.java
package com.example.web;

import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.example.domain.Customer;
import com.example.form.CustomerForm;
import com.example.service.CustomerService;

@Controller
@RequestMapping("customers")
public class CustomerController {

	@Autowired
	CustomerService customerService;

	@ModelAttribute
	CustomerForm setUpForm() {
		return new CustomerForm();
	}

	～略～

	@RequestMapping(value = "create", method = RequestMethod.POST)
	String create(@Validated CustomerForm form, BindingResult result, Model model) {
		if (result.hasErrors()) {
			return list(model);
		}
		Customer customer = new Customer();
		BeanUtils.copyProperties(form, customer);
		customerService.create(customer);
		return "redirect:/customers";
	}
	～略～
}
```

1. @ModelAttributeアノテーションを利用して、受け取りたいFormクラスを初期化してreturnします。このアノテーションがついているメソッドは、このクラスのRequestMappingアノテーションがついたいずれかのメソッド実行前に呼ばれます(そのメソッドの中でそのFormを利用している・していないは関係なし)。なお、このアノテーションが付いたメソッドの戻り値は自動でリクエストスコープに設定され(つまり、JSPやThymeleafから参照できる)、Modelオブジェクトに追加されます。Modelオブジェクトに追加される時の属性名はデフォルトで、クラス名の先頭を小文字にした値になります。Modelオブジェクトとは、Springが用意するMapオブジェクトで、Viewに渡すオブジェクトを設定します。
2. 実際にFormを利用するメソッド(ここではcreateメソッド)の引数でFormクラスのインスタンスを受け取ります(この時点で既にFormには画面上での入力値は入っている状態です)。

以上です。