ThymeleafからSpringのBeanを直接参照する例

# Thymeleaf-Springプラグインの恩恵により直参照する

Thymeleafのテンプレート内から、Springのコンテキスト管理にあるBeanを呼び出せます。

用途としては、以下のような生年月日の入力などのように定型のリストボックスやチェックボックスの値を、`@Service`や`@Repository`などで定義したクラスのpublicメソッドから値を取得して、それを表示できます。必要になるたびに`@Controller`を経由することや、`@ModelAttribute`を定義する必要もありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13268/c033a0f7-a790-0a03-3beb-1be03d485897.png)

## Controllerの実装

Controllerにてリストボックスの内容を表示する際には、例えばこのようになるでしょう。

```java:ListboxController.java
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/listbox")
public class ListboxController {
	@GetMapping
	public ModelAndView display(ModelAndView mnv) {

		mnv.addObject("listbox", createListbox());
		mnv.addObject("month", 8);
		mnv.setViewName("listbox");
		return mnv;
	}

	@PostMapping
	public ModelAndView selected(ModelAndView mnv, @RequestParam Integer month) {

		mnv.addObject("listbox", createListbox());
		mnv.addObject("month", month);
		mnv.setViewName("listbox");
		return mnv;
	}

	List<Integer> createListbox() {
		return IntStream.rangeClosed(1, 12).boxed().collect(Collectors.toList());
	}
}
```

装飾部分を一切抜いたHTMLテンプレートは、次のようになります

```listbox.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
	<div>
		リストボックスの表示・送信
		<form th:action="@{/listbox}" method="post">
			<select name="month" class="form-control">
				<option th:each="value : ${listbox}" th:value="${value}" th:text="${value}" th:selected="${month == value}" />
			</select>
			<input type="submit"/>
		</form>
	</div>
</body>
</html>
```

この `mnv.addObject("listbox", createListbox());` を必要なすべてのControllerに記載するのでも十分ですが、冒頭にも記載したSpringのBeanたちをThymeleafから直接呼び出すことで、実装の手間を省いてみます。

今回の例に挙げた、1~12の数値を格納したListを返すBeanを定義します。

```ListboxRepository.java
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.springframework.stereotype.Repository;

@Repository("listbox")  // bean名：listboxでSpringのコンテナへ格納
public class ListboxRepository {
	public List<Integer> createListbox() {
		return IntStream.rangeClosed(1, 12).boxed().collect(Collectors.toList());
	}
}
```

続いて、Controllerの実装から先ほどのlistboxを消します。(わかりやすく、行コメントで記載しています）

```ListboxController.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/listbox")
public class ListboxController {
	@GetMapping
	public ModelAndView display(ModelAndView mnv) {

		//mnv.addObject("listbox", createListbox());
		mnv.addObject("month", 8);
		mnv.setViewName("listbox");
		return mnv;
	}

	@PostMapping
	public ModelAndView selected(ModelAndView mnv, @RequestParam Integer month) {

		//mnv.addObject("listbox", createListbox());
		mnv.addObject("month", month);
		mnv.setViewName("listbox");
		return mnv;
	}

//	List<Integer> createListbox() {
//		return IntStream.rangeClosed(1, 12).boxed().collect(Collectors.toList());
//	}
}

```

HTMLテンプレートでは、Springのコンテナから参照するように記述を変えます。
`@Repository`で登録した listbox で参照できますから

```listbox.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
	<div>
		リストボックスの表示・送信
		<form th:action="@{/listbox}" method="post">
			<select name="month" class="form-control">
				<option th:each="value : ${@listbox.createListbox()}" th:value="${value}" th:text="${value}" th:selected="${month == value}" />
			</select>
			<input type="submit"/>
		</form>
	</div>
</body>
</html>
```

のように、`${@listbox.createListbox()}` でメソッドを実行した結果が得られます。

## 応用例として

日付入力の年月日のような固定値だけでなく一定期間は値が固定であるような項目、例えばシステムやサービスで持っているがWebAPIで公開されていないような情報で、更新頻度の少ない一覧、例えば従業員一覧や施設一覧情報を表示したい場合などに使えるでしょう。

こうすることで、Controllerで行いたい処理とは別のスコープで持たせることができ、Controllerの責務と実装は軽くできます。