Tambourine作業メモ

主にスキル習得のためにやった作業のメモ。他人には基本的に無用のものです。

Spring Frameworkでイチからアプリを作ってみる(6) Thymeleafのチュートリアルで構文を学ぶ その3

引き続き、Thymeleafのチュートリアルを続ける。6章の「繰り返し」と7章の「条件の評価」を読む。 やっと欲しかったものが来た感じ。

Tutorial: Using Thymeleaf (ja)

繰り返し処理の基礎

構文自体に驚きはない

<tr th:each="prod : ${prods}">
  <td th:text="${prod.name}">Onions</td>
  <td th:text="${prod.price}">2.41</td>
  <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

prodsには当然Listが入っているのだ。IteratorやMapでもよい。

繰り返しステータスの保持

Rubyのeach_with_indexのようなことをしたかったら、`th:each="prod, iterStat : ${prods}"のようにするとindexやらcountやらを属性に持ったオブジェクトがiterStatに入る。こいつを書かなかったら、デフォルトではprodStatのように変数の後ろにStatって付いたものが勝手に作られる。

データの遅延取得による最適化

LazyContextVariableを継承していると、必要があるときまでデータが展開されない。

単純な条件:“if”と“unless”

th:ifが偽ならそれが付けられたエレメントは展開した結果から抜かれる。ここでTruelyなものは

  • null
  • 0
  • false
  • “false” | “off” |“no”

じゃないものである

スイッチ文

Switchもある。構文に特に違和感はない

<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administrator</p>
  <p th:case="#{roles.manager}">User is a manager</p>
  <p th:case="*">User is some other thing</p>
</div>

Spring Frameworkでイチからアプリを作ってみる(5) Thymeleafのチュートリアルで構文を学ぶ その2

引き続き、Thymeleafのチュートリアルを続ける。5章の 属性値の設定を読む。

Tutorial: Using Thymeleaf (ja)

任意の属性に値を設定

th:class="|${hoge}|"の代わりに`th:attr="class=|${hoge}|"と書くことが出来る。

特定の属性に値を設定

でもだいたいはth:classのようなものが定義されているから大丈夫・・・つか、定義がないとダメなのか。

複数の値を同時に設定

なんか特殊な定義もある

前後に追加

th:attrappendth:attrprependっつーのもある。名前から推測出来るとおり。

固定値の真偽値属性

<input type="checkbox" name="option2" checked />

のcheckedを付けたり外したりするためのth:checkedがある。falseをセットすると、属性自体が消える。

任意の属性に値を設定(デフォルト属性プロセッサー

定義されていないth:xxxxxxという属性になる。なら、なんで定義した・・・

HTML5フレンドリーな属性や要素名のサポート

とりあえず、知る必要はなさそうなのでパス

やっと5章が終わって、次回はおまたせの繰り返しだ

Spring Frameworkでイチからアプリを作ってみる(4) Thymeleafのチュートリアルで構文を学ぶ

引き続き、Thymeleafのチュートリアルを続ける。4章のスタンダード式構文を読む。なんだ「スタンダード式構文」って。

Tutorial: Using Thymeleaf (ja)

メッセージ

ひきつづき、メッセージについて。メッセージにパラメータを入れたい場合、どうするか。

こんなメッセージプロパティに対して、

err.nofile=ファイル:{0}が存在しません。

こんな感じでパラメータを渡せる。

<p th:text="#{err.nofile(${filename})}"></p>

メッセージキーも変数で渡せるので、エラー表示エリアに好きなメッセージを表示できる。

変数

${xxx}で取ってきてるものは、実際はctx.getVariable("xxx")である。しかし、${session.user.name}((User) ctx.getVariable("session").get("user")).getName()のショートハンドである。

ふーん・・・ダサいって印象しかないが・・・まあ、いろいろと仕方がないのである。たぶん。

このスコープのなかでアクセス出来るものにはctxやsession以外にもいろいろあって、Appendix Aにまとまっている。

他に式ユーティリティというものもある。上のAppendix Aのすぐ下にAppendix Bとしてまとまってる。ま、なんかヘルパがあると。

アスタリスク構文

#{xxx}${xxx}に続いて、次は*{xxx}の話。いっぱいあるネ

*{xxx}VB.netのWithみたいなもので、あるオブジェクトの属性をまとめて表示したいよというような時に使うらしい。チュートリアルにある例を見れば、まあ、わかる。th:objectじゃなくてth:withにすればよかったのにね。ま、VBに寄せても誰も喜ばないか。

  <div th:object="${session.user}">
    <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
    <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
    <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
  </div>

リンクURL

まだ続くぞ。次は@{xxx}だ。URL専用の構文らしい。必要性はよくわからない・・・

フラグメント

まだあるのかよ。次は~{xxx}だって。フラグメントというものに関わるらしい。しかしながら、チュートリアルの説明文はまったく意味不明だ。

フラグメント式を使用すると、簡単にマークアップのフラグメントを指定したり、テンプレート内でフラグメントを移動したりすることができます。これによって、フラグメントを複製したり、他のテンプレートに引数として渡したりすることができるようになります。

無視して次に進むことにする。ちなみに、ここのサンプルでth:withが出てくるのでこれはアスタリスク構文には使えなかったようだ(笑)

リテラル

  • 文字列リテラル'xxx'("xxx"で書かれる属性のvalueに入れる必要があるから)。ただし、[A-z0-9\.\-\_\[\]]だけの文字列はシングルクォートで囲む必要がない(リテラルトークンというらしい)
  • 数値リテラルは数字そのまま
  • 真偽値はtrueとfalse。${xxx}の中に書くとJavaの処理系で処理されてしまい、Thymeleafのリテラルだとは扱われないので注意
  • nullはnull

文字列の結合

+で結合できる・・・

リテラル置換

・・・が|xxx|の中ならば${xxx}を書けば置換されるので+を使う必要はない

算術演算子

まあ、どうでもいい

比較演算子と等価演算子

比較演算子として<が使えるけど、HTMLの中では&ltを使おうねと。ダサっ

条件式

比較演算子や等価演算子を使って真偽値を得て何がうれしいかというと、条件式を使えるから。th:ifみたいなものも使えるし、なんなら属性に対してth:class="${row.even}? 'even' : 'odd'"みたいな3項演算子みたいなものを使ってもいい(この3項演算子っぽい記法を条件式と呼んでいるのかな?)

デフォルト式(エルビス演算子

Groovyのエルビス演算子( a ? a : ba ?: bと書くショートハンド。プレスリーのリーゼントに似てるから)もある。

処理なしトーク

_が評価されると、それを指定されたth:xxxは作用しなくなり、テンプレートに書いたデフォルトが使われる。

データ変換/フォーマット

${{xxx}}の様に2重にすると、xxxにtoString()(もしくは、Springではなんか定義されたなにか)が実行されて文字列になってからテンプレートの評価に回される。ややこしいね

プリプロセッシング

ここまでも十分面倒くさかったが、さらにマクロっぽいものもある。要するに、Themeleafでやる処理(式ユーティリティとか)を${xxx}の中身より先に評価したいときに使う。まあ、とりあえず使わないわ。

というわけで、長かった・・・次は5章。まだ繰り返し出てこないぞ。

Spring Frameworkでイチからアプリを作ってみる(3) Thymeleafのチュートリアルのしょっぱなで出鼻をくじかれる。

前回、Thymeleafの入門ガイドをやってみた

spring.pleiades.io

これでThymeleafが動いていることは確認できたんだけど、Thymeleafの普通の使い方を勉強したい。最低限でも繰り返しの作り方わからないといかんよな。

ひとまず、公式だろうということで、Thymeleafの公式のチュートリアルを読む。

Tutorial: Using Thymeleaf (ja)

知りたいのはどっちかというとシンタックスなんだけど、まずJavaEE標準で作られたサンプルを落としてこいという流れになっている。うーん・・・読み飛ばそう。テンプレートエンジンの設定とかあるけど、たぶんこれSpring Bootだと違うし。というわけで、3章から読むことにする。3章はメッセージの多言語対応である。メッセージのプロパティファイルを複数言語分用意して、ブラウザ設定ごとにそれぞれの言語のバージョンが読まれるようにする。

テンプレートがhome.htmlならファイルはhome.propertiesとして、テンプレートと同じところに置けと書いてある。

greeting.htmlをこんな感じにして・・・

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<boby>
    <p th:text="|Hello, ${name}!!!|" >Hello, World!!!</p>
    <p th:text="#{home.welcome}">Welcome!</p>
</boby>
</html>

テンプレートと同じ位置にテンプレートと同じ名前で置いて・・・

ファイル配置

動かすと・・・

メッセージファイルが読まれていない

ダメですね

まあ、そんな気はした。というわけで、たぶん見ているガイドが違う。探してみて、こっちじゃないかという気がする。

Tutorial: Thymeleaf + Spring

こっちはこっちでBeanの設定がいろいろ並んでいて、いや、こんなのどうせ初心者には教えられないから、シンタックスの話にならねぇかな・・・と思う。5章になって、やっとテンプレートの中身の話になる。

で、同じように最初にメッセージプロパティの話になり、「このサンプルアプリでは以下のようにMessageSource beanが設定されているので、Messages.propertiesをclasspathに置いてね」と書いてある。こんな設定のことらしい。

@Bean
public ResourceBundleMessageSource messageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("Messages");
    return messageSource;
}

私はこの設定をしていないのだから、この設定で動きはしないのではないかと思うけども、とりあえず、greeting.propertiesをMessages.propertiesにリネームして、resource直下に移動してみる。

メッセージファイルが読まれた

動いた!キモい!

いろいろとググってみると、application.propertiesの spring.messages.basenameに指定すれば動くよと書いてあるページを見かける。Spring Bootのアプリケーションプロパティ設定一覧をチェックしてみる

Spring Boot アプリケーションプロパティ設定一覧 - リファレンス

spring.messages.basenameのディフォルトはmessagesだと書いてある。ということは、大文字小文字を区別しない・・・?application.propertiesに設定してみたり、大文字小文字取り混ぜてみたりと試してみて、大文字小文字は区別しないし、このプロパティでファイルを切り替えることが出来ること、ちゃんと_jaがなければデフォルトが読み込まれることもわかった。とりあえず、小文字の方が好みなので、小文字のmessages.propertiesにしておこう。

うーむ、いろいろとわからないことが多いけど・・・いったん、元のThymeleafのチュートリアルに戻るか・・・。4章からやればいいような気がする

ちなみに、いろいろググっている時に以下の様なQiita記事を見つけて、ここに私の知りたいことは全部簡潔にまとまっているのでこれで十分な気もしてきたんだけど、ちょっと古いし、一応、本家をたどるかな・・・

qiita.com

なんだかよくわからないけどtDiaryが動かなくなったので、いろいろやった

何が原因かわからないが、手元でbrew upgradeをつい出来心でやったところ、ローカルで動かしているtDiaryが起動しなくなった。エラーを見ながらいろいろとごちゃごちゃいじって、もう何をやったかわからなくなってしまった・・・

覚えていることをメモっておく。

まず、Rubyのバージョンを3.3にしたことによって、エラーになったようだった。何が文句を言っていたのかは忘れてしまった。そこで、tdiaryのインストールディレクトリで、bundle updateをした。tDiaryのバージョン含めもろもろのバージョンが上がり、エラーは消えた。けど、いろいろな副次的な問題が出た。

まず、rustcのバージョンが古いと誰かが文句を言った。これも誰が文句を言ったのかは忘れてしまった・・・。ちゃんと作業ログを取らないのは本当によくない。rustupを使ってRustを最新化し、とりあえずOK。いまどきはRustでnative extensionを作るgemがあるんだなあ。次に、raccがないと怒られた。raccは3.3でbundled gemになったからなので、Gemfile.localに追記する。rackupもないと怒られたので、同じようにGemfile.localに足した。

次に、tdiary-style-markdownがエラーを出した。CommonMarkerというクラスがないぞという。commonmarkerは最新がインストールされている。GitHubを見に行くと、commonmarkerで定義されているクラスはCommonmarker(MarkerのMが小文字)だった。tdiary-style-markdownの最終更新は2018年ぐらいなので、そのころのcommonmarkerのコードを見てみると、当時はCommonMarkerだったようだ。名前を変えるぐらいなので全然違うものになっていてtdiary-style-markdownを直すのはかーなりしんどそうだ。そもそも、tDiary2.xでしか動かないと明言されているし・・・。結局、tDiaryはRDで書いているので、Gemfile.localから`gem 'tdiary-style-markdown'の一行を消した。

これでとりあえず、が起動した。なんだかんだ、けっこうな時間ハマってしまった。

しかし、なんでいまさらこんなのに引っかかったんだろう。不思議だ。

Spring Frameworkでイチからアプリを作ってみる(2) 渡されたものをそのまま返す

まだtextしか返していないので、HTMLを返せるようにしよう。というわけで、

入門 | Spring MVC で Web コンテンツの提供

を読んでいこう。

といっても、このページに書いてあるのは大した話じゃない。前回作ったRestControllerを普通のコントローラーにして、受け取ったパラメータをそのまま画面に返す様なメソッドを作る。こんな感じ。

@Controller
public class HelloWorldController {

    @GetMapping("/greeting")
    public String greeting(
        @RequestParam(name="name", required = false, defaultValue = "World")
        String name, Model model ){
        
            model.addAttribute("name", name);
            return "greeting";
    }
    
}

とはいえ、よくわからないことはある。

@GetMappingでこのエンドポイントに対してこのメソッドが紐付くのはわかる。引数に@RequestPramで何をとるか指定するのもわかる。で、メソッドにorg.springframework.ui.Modelが渡ってくる。パッケージ名にuiと書いてあるのだから、これはView Modelなんだろう。なので、Modelに渡ってきた引数を詰めておくのもわかる。

以下がわからない。

  1. テンプレートとどこで紐付けているのか
  2. なんでこのメソッドはStringを返すのか

一方、テンプレートの方は、こんな感じだ

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<boby>
    <p th:text="|Hello, ${name}!|" />
</boby>
</html>

なんとなくわからなくもないけど、htmlタグにネームスペースを書いているので、汎用のテンプレートじゃなくて、HTMLに特化した仕組みなんだろうか。まあ、<p th:text=...のように書いて、これが<p>...</p>になるわけだから、何らかのそういうものが必要になりそうだ。また、|で囲っているのはどういう意味なのか・・・この中がエスケープされるよと言うことなのか・・・。Thymeleafのドキュメントを読んでみないとわからない。まあ、テンプレートの仕組みもいくつも見てきたから、そんな別に変わったものではないだろうと思っているけど。

Spring Frameworkでイチからアプリを作ってみる(1) Hello World

という勉強会をやることになった。そういうことをやってみたい、教えてほしいという若者が多いのである。

そういう若者を相手にすると、勉強になるのは私である。若者は私がちょちょいでそんなことが出来ると思っているようだが、そんなことやる機会は普通に仕事していたらないのである。だいたい困っている現場に呼ばれるだけだからな。だいたいの知識はあるつもりだけども断片的だし、最初からやるとしたらどうするのがベストとか、よく知らないのである。というわけで、一番ためになっているのは私であり、若者をダシに知識の整理をして楽しもう。

とはいえ、プログラミングの学習も、環境を作ってHello Worldするまでが一番難しい。一度、ビルド出来てしまえばそこからは買ってきた本の通りになるけど、ビルド出来るようになるまでに起きることは自分の環境に依存した出来事だからだ。

というわけで、なーんもよくわかってない若者前提で、手順を考える。今日はWindows環境前提でやる。ぶっちゃけ私もなんもわかっていない。

まずは、Javaをインストールする。これは記事に書いた。好きなものをwingetしてくれればよい。GitもWingetすればいいはず。ここまでは私のPCにも入ってた。

次はGradleかな。これはwingetできない。というかダウンロードしてフォルダに展開するだけ。公式はC:\Gradleを作ってそこに入れろとしているが、趣味でホームディレクトリの下に入れる事にする。~/bin/gradleの下に公式からダウンロードしたzipを解凍したものをそのまま入れる。

そして、これを環境変数のPATHへセットする。どうやっても難しいが、PowerShellでやる方法をここでは書いておこう。

PS C:\Users\Tambourine> [System.Environment]::SetEnvironmentVariable('PATH', $Env:PATH + 'C:\Users\Tambourine\bin\gradle\gradle-8.6\bin;', 'User')

PowerShellはホント、しっくりこないよ・・・

これで、ターミナルを再起動すればPATHが設定されているはず。設定したターミナルでは$Env:PATHの値は変化しないので注意。

PS C:\Users\Tambourine> gradle -v

Welcome to Gradle 8.6!

Here are the highlights of this release:
 - Configurable encryption key for configuration cache
 - Build init improvements
 - Build authoring improvements

For more details see https://docs.gradle.org/8.6/release-notes.html


------------------------------------------------------------
Gradle 8.6
------------------------------------------------------------

Build time:   2024-02-02 16:47:16 UTC
Revision:     d55c486870a0dc6f6278f53d21381396d0741c6e

Kotlin:       1.9.20
Groovy:       3.0.17
Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM:          20.0.2 (Eclipse Adoptium 20.0.2+9)
OS:           Windows 11 10.0 amd64

JDKとGradleが入ったらいよいよSpringの出番だ。Getting Started的なドキュメントを読むと、とりあえずSpring Initializrでひな形を作れと書いてある。Springはただでさえ黒魔術が多いのに、Spring Bootすると何がどういう仕組みで動いているのかが黒魔術ラッパーで覆われてしまうので気が重いが、初心者は公式ドキュメントの通りにやるのが基本だ。

というわけで、 https://spring.pleiades.io/guides/gs/spring-boot を見ながら進もう。

まずはSpring Initializrでポチポチする。依存ライブラリは3つだけ入れた

Spring Initializr

この選択があっているのかどうかはよくわからない。生成されたコードをダウンロードして、vscodeで開いてみる。開くとJavaとかGradleのプラグインをいれたらどうかねと言われるので、入れる。言われるがままである。

では、コントローラーを作ってみよう。

といっても、ドキュメントをまんまパクる

package one.tmbrms.readingsns;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {
    
    @GetMapping("/")
    public String index(){
        return "Hello World!";
    }
    
}

とりあえず、これだけ作れば、GradleでbootRunタスクを実行すると、localhost:8080 でHello Worldは表示される。素晴らしい。

ただし、ドキュメントはアプリケーションクラスをもっとちゃんとせいと書いてある。で、CommandLineRunnerメソッドを追加して、beanの一覧を取得している。

 @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {

            System.out.println("Let's inspect the beans provided by Spring Boot:");

            String[] beanNames = ctx.getBeanDefinitionNames();
            Arrays.sort(beanNames);
            for (String beanName : beanNames) {
                System.out.println(beanName);
            }

        };
    }

これをアプリケーションクラスに追加する。すると、起動時に以下が出力される。

Let's inspect the beans provided by Spring Boot:
applicationAvailability
applicationTaskExecutor
basicErrorController
beanNameHandlerMapping
beanNameViewResolver
characterEncodingFilter
classPathFileSystemWatcher
classPathRestartStrategy
commandLineRunner
conditionEvaluationDeltaLoggingListener
conventionErrorViewResolver
defaultServletHandlerMapping
defaultTemplateResolver
defaultViewResolver
dispatcherServlet
dispatcherServletRegistration
error
errorAttributes
errorPageCustomizer
errorPageRegistrarBeanPostProcessor
fileSystemWatcherFactory
fileWatcher
flashMapManager
forceAutoProxyCreatorToUseClassProxying
formContentFilter
handlerExceptionResolver
handlerFunctionAdapter
helloWorldController
httpMessageConvertersRestClientCustomizer
httpRequestHandlerAdapter
jacksonObjectMapper
jacksonObjectMapperBuilder
jsonComponentModule
jsonMixinModule
jsonMixinModuleEntries
lifecycleProcessor
liveReloadServer
liveReloadServerEventListener
localeCharsetMappingsCustomizer
localeResolver
mappingJackson2HttpMessageConverter
messageConverters
multipartConfigElement
multipartResolver
mvcContentNegotiationManager
mvcConversionService
mvcHandlerMappingIntrospector
mvcPathMatcher
mvcPatternParser
mvcResourceUrlProvider
mvcUriComponentsContributor
mvcUrlPathHelper
mvcValidator
mvcViewResolver
optionalLiveReloadServer
org.springframework.aop.config.internalAutoProxyCreator
org.springframework.boot.autoconfigure.AutoConfigurationPackages
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfiguration
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonMixinConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration
org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration
org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$SimpleAsyncTaskExecutorBuilderConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$TaskExecutorBuilderConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$TaskExecutorConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$ThreadPoolTaskExecutorBuilderConfiguration
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$SimpleAsyncTaskSchedulerBuilderConfiguration
org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$TaskSchedulerBuilderConfiguration
org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$ThreadPoolTaskSchedulerBuilderConfiguration
org.springframework.boot.autoconfigure.thymeleaf.TemplateEngineConfigurations$DefaultTemplateEngineConfiguration
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$DefaultTemplateResolverConfiguration
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafWebMvcConfiguration
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafWebMvcConfiguration$ThymeleafViewResolverConfiguration
org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration
org.springframework.boot.context.internalConfigurationPropertiesBinder
org.springframework.boot.context.properties.BoundConfigurationProperties
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor
org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.methodValidationExcludeFilter
org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration
org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration
org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$RestartConfiguration
org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.event.internalEventListenerFactory
org.springframework.context.event.internalEventListenerProcessor
parameterNamesModule
preserveErrorControllerTargetClassPostProcessor
propertySourcesPlaceholderConfigurer
readingsnsApplication
requestContextFilter
requestMappingHandlerAdapter
requestMappingHandlerMapping
resourceHandlerMapping
restClientBuilder
restClientBuilderConfigurer
restClientSsl
restTemplateBuilder
restTemplateBuilderConfigurer
restartingClassPathChangedEventListener
routerFunctionMapping
server-org.springframework.boot.autoconfigure.web.ServerProperties
servletWebServerFactoryCustomizer
simpleAsyncTaskExecutorBuilder
simpleAsyncTaskSchedulerBuilder
simpleControllerHandlerAdapter
spring.devtools-org.springframework.boot.devtools.autoconfigure.DevToolsProperties
spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties
spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties
spring.lifecycle-org.springframework.boot.autoconfigure.context.LifecycleProperties
spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
spring.sql.init-org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties
spring.ssl-org.springframework.boot.autoconfigure.ssl.SslProperties
spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties
spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
spring.thymeleaf-org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties
spring.web-org.springframework.boot.autoconfigure.web.WebProperties
sslBundleRegistry
sslPropertiesSslBundleRegistrar
standardJacksonObjectMapperBuilderCustomizer
stringHttpMessageConverter
taskExecutorBuilder
taskSchedulerBuilder
templateEngine
themeResolver
threadPoolTaskExecutorBuilder
threadPoolTaskSchedulerBuilder
thymeleafViewResolver
tomcatServletWebServerFactory
tomcatServletWebServerFactoryCustomizer
tomcatWebServerFactoryCustomizer
viewControllerHandlerMapping
viewNameTranslator
viewResolver
webServerFactoryCustomizerBeanPostProcessor
websocketServletWebServerCustomizer
welcomePageHandlerMapping
welcomePageNotAcceptableHandlerMapping

この長いリストを見ると、だいぶ気分が悪くなる。が、まあ、仕方がないね。ちなみに、この@Beanを付けたメソッドは、HelloWorldControllerに最初間違えて付けちゃった。でも、動く。キモいねー

次が単体テストなんだが・・・エンドポイントのテストをしようとしている。キモい。個人的なことを言えば、RESTのエンドポイントのテストは単体テストだとは思わない。これはE2Eとユニットテストの中間に位置するもので、Postmanとかでテストしたい。まあ、Postman使わずにテストできたら便利じゃないかと言われればそうだけども、若者にこれをユニットテストだと思ってほしくないので、ここはとりあえず飛ばす。こういうものがあるんだということは覚えておこう。

というか、build.gradleの依存のところに

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

勝手に入っているspring-boot-starter-testを消したいなあ・・・

さて、その次には監視とかのアクチュエーターを追加するところ。 あー、こういうのは便利だね。でも、とりあえず飛ばしても良いだろう。

starterと仲良くなるには、だいぶ時間がかかりそうだなー

というわけで、いったんここまでで終わりにしようか。