SpringBoot3でwebページ作成_Groovy templates

<h1>はじめに</h1>
springBoot3勉強中に知った<b>「groovy templates」というテンプレートエンジン</b>。<br>
ほかのテンプレートエンジン(Thymeleaf等)と違ってHTMLタグではなくGroovyのコードで画面表示をつくるのが面白そう。ということで試してみました。<br>

<h1>まず参考文献</h1>
「Spring Boot 3 プログラミング入門」(<a src="https://www.shuwasystem.co.jp/book/9784798069166.html">https://www.shuwasystem.co.jp/book/9784798069166.html</a>)
<br>⇒Chapter3, Chapter4

<h1>環境</h1>
<ul>
    <li>開発環境：Eclipse 2022-12 (4.26.0)</li>
    <li>Java 17.0.5</li>
    <li>フレームワーク：SpringBoot 3.1.0</li>
    <li>ビルドツール：gradle</li>
</ul>
<h1>準備</h1>
<h4>プロジェクト新規作成</h4>
ファイル > 新規 > Spring スターター・プロジェクト

![新規作成.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719849/576f5708-305f-7d8f-00cb-d1ec82812fd1.jpeg)
<br>

<h4>gradle</h4>
dependenciesに追加した
<ul>
    <li>「spring-boot-starter-groovy-templates」・・・Groovy templatesテンプレートエンジン</li>
    <li>「spring-boot-starter-web」・・・Webパッケージ</li>
    <li>「spring-boot-starter-data-jpa」・・・DBアクセスするために必要</li>
    <li>「spring-boot-starter-test」・・・コードテストするために必要</li>
</ul>

```gradle:build.gradle
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.2'
	id 'io.spring.dependency-management' version '1.1.2'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-groovy-templates'
	implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	runtimeOnly 'com.h2database:h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}
```

<h4>設定ファイル(groovyの設定)</h4>
<ul><li>「cache=false」・・・キャッシュOFF</li>
<li>「suffix=.tpl」・・・ビュー名に追加される拡張子はtpl</li>

```properties:application.properties
spring.groovy.template.cache=false
spring.groovy.template.suffix=.tpl
```


<h4>アプリケーションクラス(自動生成されたまま)</h4>


```java:GroovyStudyApplication.java
package com.example.groovy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GroovyStudyApplication {

	public static void main(String[] args) {
		SpringApplication.run(GroovyStudyApplication.class, args);
	}

}
```

<h4>テーブル</h4>
繰り返し表示させてみるAccountテーブルの設定

```java:Account.java
package com.example.groovy;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name="account")
public class Account {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	private long id;
	
	@Column(length = 50, nullable = false)
	private String name;
	
	@Column(length = 200, nullable = true)
	private String mail;

	public Account() {
	}
	
	public Account(String name, String mail) {
		this.name = name;
		this.mail = mail;
	}

	public long getId() {
		return id;
	}
	public String getName() {
		return name;
	}
	public String getMail() {
		return mail;
	}
}
```

```java:AccountRepository.java
package com.example.groovy.repositories;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.groovy.Account;

@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {

}
```

<h4>コントローラークラス</h4>
<table>
        <tr>
            <td>initメソッド</td>
            <td>インスタンス生成後に呼び出される(PostConstructアノテーションによって)<br>Accountテーブルにデータ投入している</td>
        </tr>
        <tr>
            <td>getDataメソッド</td>
            <td>GET /getData アクセス時に呼び出される<br>mav.setViewName("getData");で画面表示に使うテンプレートを指定(getData.tpl)</td>
        </tr>
</table>


```java:IndexController.java
package com.example.groovy.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.ModelAndView;

import com.example.groovy.Account;
import com.example.groovy.repositories.AccountRepository;

import jakarta.annotation.PostConstruct;

@Controller
public class IndexController {
	
	@Autowired
	AccountRepository repository;

	@PostConstruct
	public void init() {
		List<Account> accountList = new ArrayList<>();
		accountList.add(new Account("ichiro", "example1@example.com"));
		accountList.add(new Account("jiro", "example2@example.com"));
		accountList.add(new Account("saburo", "example3@example.com"));
		
		repository.saveAllAndFlush(accountList);
	}

	@GetMapping("/getData")
	public ModelAndView getData(@ModelAttribute("formModel") Account account, ModelAndView mav) {
		mav.setViewName("getData");
        mav.addObject("title", "タイトル表示");
		mav.addObject("message", "メッセージ表示");
        mav.addObject("flag1", true);
        mav.addObject("flag2", false);

		Iterable<Account> list = repository.findAll();
		mav.addObject("data", list);
		
		return mav;
	}
}
```

<h4>テンプレート</h4>

```groovy:getData.tpl
yieldUnescaped '<!DOCTYPE html>'

html(lang:'ja') {
	head {
		meta(charset:"UTF-8")
		title("繰り返し")
		link(rel:"stylesheet", type:"text/css",
			href:"https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css")
	}
	body(class:"container"){
		h1(class:"display-4 mb-4", title)
		p(message)
		div (class:"alert alert-primary") {
			if (flag1) {
				p("flag1がtrueの時に出力されるよ")
			} else {
				p("flag1がfalseだったら出力")	
			}
			if (flag2) {
				p("flag2がtrueの時に出力されるよ")
			} else {
				p("flag2がfalseだったら出力")	
			}
		}

		div (class:"card") {
			div (class:"card-body") {
				h3(class:"card-title", "アカウント一覧")
				data.each {
					p(class:"card-text", it.id + "／" + it.name + "／" + it.mail)
				}
			}
		}
	}
}
```
⇒実行してみる！(プロジェクトを右クリック > 実行 > Spring Boot アプリケーション)

<h1>表示できた</h1>

![画面.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719849/8af52b42-d5f1-a6f7-8e4c-86301ad61055.jpeg)