@NotBlank、@NotEmpty、@NotNullの挙動の違いをSpring Boot + Thymeleafで整理する

## やりたいこと
JSR-303 Bean Validationの挙動がよく分からず、現場で混乱を招いたので整理してみます。具体的には以下のアノテーションです。
・javax.validation.constraints.NotBlank (@NotBlank)
・javax.validation.constraints.NotEmpty (@NotEmpty)
・javax.validation.constraints.NotNull  (@NotNull)


## 結論
![wwwww.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/c0afb70d-25bd-3f77-6938-718662caeced.png)
※タブ以外のエスケープシーケンスは検証していませんが多分タブと同じ挙動になると思っています（多分）

全て全角スペースの場合は、チェックに引っかからないので注意が必要です。

## 検証

以下のような簡易画面で検証してみました。

```HelloController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.form.HelloForm;

@Controller
@RequestMapping(value = "/")
public class HelloController {
	
	private static final String SAMPLE_URL = "sample/Hello";

	@GetMapping
	public String index(@ModelAttribute HelloForm helloForm) {
		return SAMPLE_URL;
	}

	@PostMapping
	public String register(@Validated HelloForm helloForm, BindingResult result) {
		return SAMPLE_URL;
	}
}
```

```HelloForm.java
package com.example.demo.form;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

public class HelloForm {
	
	@NotBlank
	String notBlankField;
	
	@NotEmpty
	String notEmptyField;
	
	@NotNull
	String notNullField;
	
	public String getNotBlankField() {
		return notBlankField;
	}

	public void setNotBlankField(String notBlankField) {
		this.notBlankField = notBlankField;
	}

	public String getNotEmptyField() {
		return notEmptyField;
	}

	public void setNotEmptyField(String notEmptyField) {
		this.notEmptyField = notEmptyField;
	}

	public String getNotNullField() {
		return notNullField;
	}

	public void setNotNullField(String notNullField) {
		this.notNullField = notNullField;
	}
}
```

```Hello.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
	<form method="post" action="/" th:object="${helloForm}">
		<span th:if="${#fields.hasErrors('notBlankField')}" th:errors="*{notBlankField}" style="color: red"></span><br /> 
		NotBlankField：<input type="text" name="notBlankField" th:field="*{notBlankField}" /><br /> 
		<span th:if="${#fields.hasErrors('notEmptyField')}" th:errors="*{notEmptyField}" style="color: red"></span><br /> 
		NotEmptyField：<input type="text" name="notEmptyField" th:field="*{notEmptyField}" /><br /> 
		<span th:if="${#fields.hasErrors('notNullField')}" th:errors="*{notNullField}" style="color: red"></span><br /> 
		NotNullField：<input type="text" name="notNullField" th:field="*{notNullField}" /><br /><br />
		<input type="submit" value="Submit" />
	</form>
</body>
</html>
```
### 空文字
NotBlank、NotEmptyのみチェックに引っかかる
![空文字.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/485fdf33-777d-5953-ebaf-0eb7cb0feab3.png)
### 全て半角スペース
※NotBlankのみチェックに引っかかる
![全て半角スペース.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/7994ac26-240b-98cb-6783-84a3d380a2fc.png)
### 全て全角スペース
※NotBlank、NotEmpty、NotNull全てチェックに引っかからない
![全て全角スペース.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/af209a18-f17d-201c-2d4e-f7e026941adc.png)
ソースは以下です。
https://github.com/kenichi-nagaoka/spring-boot-form-validation-sample

以上です。