🙄

【1日1zenn - day12】Spring BootでHTMLを表示

に公開

やること

https://spring.pleiades.io/spring-boot/tutorial/first-application/
Springのチュートリアルをやろうと思います。kotlinは一旦後回し。このチュートリアルを書き換えたりしていくかも。

環境構築

必要なものは以下。

  • InteliJ
  • Java
  • Maven
  • Gradle
    全部私物PCでは入れていなかったです。

InteliJはサクッとインストール。

Java

java -version
The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.

Javaが入っていなかったので、こちらの記事を参考にしながら、
OracleのページからARM64 DMG Installer をDLしました。
バージョンは、一旦最新の23にしてます。

java -version
java version "23.0.1" 2024-10-15
Java(TM) SE Runtime Environment (build 23.0.1+11-39)
Java HotSpot(TM) 64-Bit Server VM (build 23.0.1+11-39, mixed mode, sharing)

大丈夫そう。

Maven

次にMaven。
https://sukkiri.jp/technologies/devtools/maven/apache-maven-installmacos.html
これに従ってDLした後、以下を実行しました。

$ cat << EOF >> ~/.zshrc

export PATH=$PATH:~/apache-maven-3.9.6/bin 

EOF

$ . ~/.zshrc

すると以下のエラーが。

% . ~/.zshrc
# Load pyenv automatically by appending
# the following to 
# your shell's login startup file (for login shells)
# and your shell's interactive startup file (for interactive shells) :

export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

# Restart your shell for the changes to take effect.

(eval):1: command not found: “”export

exportコマンドの引用符の書き方が間違ってるっぽい。
ちょうど昨日vimをいじったので、vimで開いてみる。

vim ~/.zshrc 

そしてaを押すとインサートモードになるので、その状態で引用符を修正した。
escapeを押してインサートモードを終えてから、:wqで保存して終了。

で、改めて. ~/.zshrcを実行してみると以下だった。

% . ~/.zshrc
# Load pyenv automatically by appending
# the following to 
# your shell's login startup file (for login shells)
# and your shell's interactive startup file (for interactive shells) :

export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

# Restart your shell for the changes to take effect.

なんかこれ昔Pythonの環境設定でミスった残穢な気がするな。
言われた通りのコードを貼り付けたりして、source ~/.zshrcでシェルに変更を反映。
すると. ~/.zshrcは正常に実行できた。

しかし。

% mvn -version
zsh: command not found: mvn

なんかmvnは使えない。DLしてる場所が悪いのかな。
いや、まず一つが、DLしたversionが/apache-maven-3.9.6じゃなかったからパスの指定が間違ってた。
あと場所も違いそう。と思って移動させたが、command not foundのまま。
そもそもコマンドがどういう仕組みで実行されるかわからん。

コマンドが実行される仕組みby GPT

  1. コマンド入力: ユーザーがターミナルで mvn -version のようなコマンドを入力します。
  2. コマンド解釈: シェル(例えば、zsh や bash)は、このコマンドを解釈します。シェルは mvn というコマンドがシステム上に存在するかどうかを調べます。
  3. PATH 環境変数: シェルは、mvn というコマンドがどこにあるのかを調べるために、PATH という環境変数を参照します。$PATH には、実行可能なファイルを探すためのディレクトリの一覧が格納されています。
    例えば、$PATH に次のような値が設定されていたとします:
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/username/apache-maven-3.9.6/bin

シェルはこの順番でディレクトリを探し、最初に mvn という名前の実行可能ファイルを見つけた場所でコマンドを実行します。

  1. 実行ファイルの呼び出し: mvn コマンドが見つかると、そのファイルが実行されます。たとえば、mvn が /Users/username/apache-maven-3.9.6/bin/mvn にある場合、このファイルが実行され、Maven のバージョン情報などが表示されます。

なるほどね。
じゃあmvmコマンドがどこに定義されているかと、今のPATHがどうなってるかを見る必要がありそう。

$ which mvn
/Users/ユーザー名/apache-maven-3.9.9/bin/mvn
$ echo $PATH
合ってそうだった

その後、試しにPATHをいじってたら、直したはずなのにそもそもwhich mvnでも見つからなくなった。

$ which mvn
mvn not found

なんでや。。

$ where apache-maven-3.9.9
apache-maven-3.9.9 not found

GUI上には明らかに存在するが、なぜか消えてる。
まあなんか、一回再起動してみようかな。

再起動したら認識されるようになった。

$ which mvn               
/Users/miyatas/apache-maven-3.9.9/bin/mvn
$ mvn -version
Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: /Users/ユーザー名/apache-maven-3.9.9
Java version: 23.0.1, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home
Default locale: ja_JP, platform encoding: UTF-8
OS name: "mac os x", version: "14.5", arch: "aarch64", family: "mac"

治った〜〜〜。
これだから環境構築は苦手なんだ。。
まあシェルについて少し理解が深まったのでヨシ。

Gradle

同様にGradleもやろう。

$ gradle --version
zsh: command not found: gradle

今はまだない。
https://gradle-org.translate.goog/install/?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja&_x_tr_pto=sc
brew install gradleを実行。

$ gradle --version   

Welcome to Gradle 8.12!
以下省略

サクッとインストールできた〜〜。感謝。

Hellow Worldまで。

続き読んでたら、なんかGradleかMavenのどちらかで良かった感じがする。
調べてみるとどっちもビルドツールで、Gradleの方が新しいっぽい。常識を一つ仕入れた。

そしてなんかSpring Initializrでやったら早そう。
以下の記事がわかりやすくて、この通りやったらKotlinでプロジェクトを作れました。
https://zenn.dev/dollaga_saiore/articles/spring_intelij_maven20240815

そしてチュートリアルにある以下を貼り付け。

package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@RestController
@SpringBootApplication
class MyApplication {

	@RequestMapping("/")
	fun home() = "Hello World!"

}

fun main(args: Array<String>) {
	runApplication<MyApplication>(*args)
}

そしてgradle bootRunを実行すると、、、、

$  gradle bootRun

FAILURE: Build completed with 2 failures.

1: Task failed with an exception.
-----------
* What went wrong:
A problem occurred configuring root project 'demo'.
> Failed to calculate the value of task ':compileJava' property 'javaCompiler'.
   > Cannot find a Java installation on your machine matching this tasks requirements: {languageVersion=21, vendor=any vendor, implementation=vendor-specific} for MAC_OS on aarch64.
      > No locally installed toolchains match and toolchain download repositories have not been configured.

* Try:
> Learn more about toolchain auto-detection at https://docs.gradle.org/8.12/userguide/toolchains.html#sec:auto_detection.
> Learn more about toolchain repositories at https://docs.gradle.org/8.12/userguide/toolchains.html#sub:download_repositories.
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
Failed to query the value of property 'buildFlowServiceProperty'.
> Could not isolate value org.jetbrains.kotlin.gradle.plugin.statistics.BuildFlowService$Parameters_Decorated@54863cf0 of type BuildFlowService.Parameters
   > A problem occurred configuring root project 'demo'.
      > Failed to calculate the value of task ':compileJava' property 'javaCompiler'.
         > Cannot find a Java installation on your machine matching this tasks requirements: {languageVersion=21, vendor=any vendor, implementation=vendor-specific} for MAC_OS on aarch64.
            > No locally installed toolchains match and toolchain download repositories have not been configured.

* Try:
> Learn more about toolchain auto-detection at https://docs.gradle.org/8.12/userguide/toolchains.html#sec:auto_detection.
> Learn more about toolchain repositories at https://docs.gradle.org/8.12/userguide/toolchains.html#sub:download_repositories.
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
==============================================================================

BUILD FAILED in 1s

Javaの言語のバージョンが21って書かれてるっぽい。
インストールしたのが23なので、以下を直した。

build.gradle.kts
java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(23)
	}
}

もう一度bootrunすると以下。

$ gradle bootRun
Kotlin does not yet support 23 JDK target, falling back to Kotlin JVM_21 JVM target

> Task :compileKotlin
Kotlin does not yet support 23 JDK target, falling back to Kotlin JVM_21 JVM target

> Task :compileKotlin FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileKotlin'.
> Inconsistent JVM-target compatibility detected for tasks 'compileJava' (23) and 'compileKotlin' (21).
  
  Consider using JVM Toolchain: https://kotl.in/gradle/jvm/toolchain
  Learn more about JVM-target validation: https://kotl.in/gradle/jvm/target-validation 

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 14s
2 actionable tasks: 2 executed

KotlinはJava23じゃ動かないっぽい?
調べてみると以下のstack overflowがあり、どうやらkotlinのバージョンを変えたらいけるかも?

plugins {
	kotlin("jvm") version "1.9.25"
	kotlin("plugin.spring") version "1.9.25"
	id("org.springframework.boot") version "3.4.1"
	id("io.spring.dependency-management") version "1.1.7"
}

これを以下に変えた。

plugins {
	kotlin("jvm") version "2.1.0"
	kotlin("plugin.spring") version "1.9.25"
	id("org.springframework.boot") version "3.4.1"
	id("io.spring.dependency-management") version "1.1.7"
}

"plugin.spring"のバージョンは変えてないけど通るかな。

 gradle bootRun

> Task :compileKotlin FAILED
e: file:///Users/miyatas/workspace/1day1zenn/20241227/src/main/kotlin/com/example/demo/DemoApplication.kt:6:2 Unresolved reference 'RestController'.
e: file:///Users/miyatas/workspace/1day1zenn/20241227/src/main/kotlin/com/example/demo/DemoApplication.kt:10:3 Unresolved reference 'RequestMapping'.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
   > Compilation error. See log for more details

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 32s

今度は通りはしたけど、エラーっぽい。
import漏れだ。
なんか普段自動でimportされてた気がするけどlintかなんかの設定によるのかなと思いながら以下に修正するとbootrunが通った。

package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping

@RestController
@SpringBootApplication
class MyApplication {

	@RequestMapping("/")
	fun home() = "Hello World!"

}

fun main(args: Array<String>) {
	runApplication<MyApplication>(*args)
}

ちゃんと表示されましたね。

なんだかんだ長かった。

HTMLを表示してみる

以下の記事を参考に進めます。
https://qiita.com/takuch/items/3dc30caf45af06a01f41

Tymeleafの以下を呼び出すように変えたい。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Hello World</title>
</head>
<body>
  <h1>Hello World</h1>
</body>
</html>

うまくいかなかったのでGPTに聞きながら進めたところ、以下のように修正したら呼び出せました。

package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.stereotype.Controller
import org.springframework.ui.Model

@Controller
@SpringBootApplication
class MyApplication {

	@RequestMapping("/")
	fun home(model: Model): String {
		return "hello"
	}
}

fun main(args: Array<String>) {
	runApplication<MyApplication>(*args)
}

変更点

  • @RestControllerではなく@Controllerを使う
    • @ControllerはHTTP Response Bodyを返す型
    • JSON形式のデータは返したい時に@RestControllerを使う
    • 参考記事
    • これ昨日Springで調べた時に腹落ちできてなかったけどそういうことかぁ。やっぱやると違うな。
    • Viewを返すみたいな話も、クリーンアーキテクチャでイメージ付いてなかったところがわかりやすくなる感じする
  • fun homeの引数としてModel型のmodelを渡す
    • ドキュメント
    • よくわからんかったからGPT曰く
      • Model 型は、Spring MVC のコントローラーメソッドに渡されるオブジェクトで、ビューにデータを渡すために使用されます。
      • Model を使って、テンプレート(例えば、Thymeleaf)にデータを渡すことができます。
      • Model 引数: コントローラーのメソッドで Model 引数を受け取ると、そこにデータを設定することができ、これをビュー(HTML テンプレート)に渡すことができます。
      • 例えば、model.addAttribute("key", value) でビューに key という名前で value を渡すことができます。
    • ということはマストじゃないのでは?と思い引数を消してみると、ちゃんと動いた。
    • GPTくんの出したコードをそのまま使った代償でした。
  • 返り値をStringにする
    • thymeleafを使っていると、返り値を文字列にした場合、src/main/resources/templatesからその名前のファイルを検索するらしい
    • <html xmlns:th="http://www.thymeleaf.org">みたいなタグ名にしたのも効いてるっぽい
    • https://qiita.com/thirai67/items/64fd18810a8ccfedd459
      • textとかifとか色々指定できるっぽい

一旦区切る

ちょっと次の予定の時間が迫ってきたので、一旦今日はここまでにします
GETとかPOSTとか、もうちょい色々明日やる。

Discussion