Chapter 02

Hello worldをHTMLで表示しよう

この章で学ぶこと

spring initializrでひな形を作れたので、それを少し動かしてみます。
thymeleafでHello Worldを表示させるまででゴールです。この過程のイメージが付くのであれば、この章は読まなくて大丈夫です。

  • IDEでプロジェクトをインポートする
  • そのまま触ってみる
  • thymeleaf(タイムリーフ)でSSR(サーバサイドレンダリング)する

IDEでプロジェクトをインポートする

さて、zipファイルのプロジェクトが手に入りましたので、これをIDE(統合開発環境。より簡単にプログラミングするためのツール)で読み込むことにします。
今回利用するIDEは、InteliJ IDEAです。有料版、無料版ありますが、無料で構いません。

有料で買う方はSamuraismさんから少しだけ安く購入できます。
さらに、Samuraismさんでは、誰か既存ユーザの紹介があればもう少しだけ安く購入ができます。
例えば、私とか。 -> 紹介リンク

より簡単なインストール方法として、JetBrainsの提供するtoolboxを使う事ができます。こちらを使えばバージョンアップが容易ですのでおすすめです。

IDEの設定

本稿はJavaの勉強をした人に向けているため、IDEのJDK設定の詳細は省略していますが、InteliJ IDEAはEclipse等と同様にSDK/JDKの設定が必要です。

もし、インストールされていない場合でも、IDEからのインストールが可能です

Lombokプラグインのインストール

今回インストールする開発ツールのうち、LombokだけはIDE側にプラグインを必要とします。

InteliJ IDEAバージョン2020.3よりLombokプラグインがIDE内臓になっています。
そのため、それ以降のバージョンではこの手順は必要ありません。

https://projectlombok.org/setup/intellij
honamiさん、ご指摘ありがとうございました。

IntelijJ IDEAの設定を開き、

Lombokプラグインをインストールしてください。

Lombokについて、特に設定は必要ありません。

zipファイルの展開

IDEがインストール,設定できたら、ダウンロードした雛形を展開し、IDEにインポートしましょう。

まず、ワーキングディレクトリ(作業場所になるディレクトリ)を作ることをおすすめします。
私の場合は~/Work/Javaがjava言語のプロジェクトを開発するための場所になりますので、zipファイルを移動させ、zipファイルを展開します。

(Linuxコマンドを使用する必要はないので、エクスプローラーやFinderで行っても大丈夫です)

展開できたら、zipファイルは不要ですので削除して構いません。

インポート

Openで対象ディレクトリを探します。

今回は展開したディレクトリを指定します。

これだけでインポートは完了します。

そのまま触ってみる

まずは、正常にプロジェクトが読み込めたか、IDEが設定できているかを確かめましょう。

IDE右上のカナヅチで、ビルドを実行します。

実行したら、右下のEventLogを開きます。
成功していそうなメッセージが表示されていれば問題ありません。

もし、赤色のエラーが表示されていれば、何らかの理由で失敗しています。
それは、おそらく開発環境に依存したものですので、エラーメッセージを翻訳したり、特徴的な英文を検索にかけたりしてみてください。

それでもわからないようなら、teratailなど質問サイトを使うのも良いでしょう。

実行してみる

正常にビルドできれば、実行してみます。
カナヅチの右にある再生マークを押してみましょう。

ここでは、BulletinBoardApplicationクラスを実行する、という意味になります。
BulletinBoardApplicationクラスはmainメソッドを見ても分かる通り、このSpringBootアプリケーションの起動を行います。

再生マークを押したら、IDE下部の(下にあるかは設定によりますが)RUNタブを開きましょう。

このように様々なログが流れています。
最後のログである、

2021-03-06 20:26:47.800  INFO 28478 --- [  restartedMain] c.m.b.BulletinBoardApplication           : Started BulletinBoardApplication in 1.663 seconds (JVM running for 2.486)

は、Started BulletinBoardApplicationの英文が示すとおり、このアプリケーションの起動成功を意味しています。

上記のログを確認したら、アプリケーションを停止しましょう。停止は、赤い四角の停止ボタンを押します。

thymeleafでSSRする

では、ブラウザ上にHello Worldを表示するために、Thymeleafを使ってみましょう。
Thymeleafそのものは、動的にHTMLをレンダリングするものです。
つまり、表示するタイミングや、人によって異なるHTMLを返すためのものなので、Hello Worldだけを表示する用途だけでは寂しいですが、最初の一歩としては十分です。

ちなみに、SSR(サーバサイドレンダリング)はサーバー側、バックエンドでHTMLを組み立てることを言います。
Vue.jsやReact.jsなど、ユーザのブラウザに配信されてからJavaScriptによってHTMLを組み立てるものをクライアントサイドレンダリングといいます。

thymeleafはHTMLをJavaで再構築し、ブラウザでレンダリングしてもらっています。
そのため、Javaアプリケーションを通してアクセスしなければ、正しくHTMLとして表示されません。

SSRするまでの流れ

今回作るのはWebアプリケーションですから、HTTPアクセスがなければ何も行いません。
そこで、今回はhttp://localhost:8080/helloにアクセスすると、Hello worldと書かれたHTMLを返すことにします。

補足

この流れを図で表すと、このようになります。

まず、http://localhostについて説明します。
これは、ざっくりいうと自分のパソコンの中のネットワークを意味しています。
今回は自身のコンピュータの上でSpringBootを動かすだけなので、アクセス先が自分のパソコンになっているということです。

http://localhostにつづく、:8080はポート番号を表しています。
ポート番号とは、アクセスされるアプリケーションそれぞれで決まっている通信を受ける窓口番号です。

これはパソコンの上だけでなく、インターネット上にあるWebサーバも例外ではありません。Webサーバが公開するポート番号として、一般的にhttpは80番ポート、httpsは443番ポートを利用することが約束されています。
これによって、ブラウザが発したhttps://~というURLがWebサーバに到達したとき、Webアプリケーション等が処理してHTMLを返してくれています。
もし、httpsの使うポート番号が定まっていなければ、URLには必ずポート番号を指定しなければいけなくなってしまいます。そうしないと、HTMLを返してくれるWebサーバとお話することが出来ませんから。

https://www.cman.jp/network/term/port/

今回の場合、SpringBootのサーブレットコンテナであるTomcatは8080番ポートで通信を待ち受けるため、HTTPデフォルトの80番ではなく、8080番を明示しています。

なぜ、このようなポート番号などという複雑な仕組みがあるのでしょうか。
それは、同時に色々な通信をさばこうとすると、その宛先のアプリケーションを明示してもらわないと、どれに案内すればよいかコンピュータもわからないためです。

なので、今回はTomcatが待ち受けている8080番にアクセスしたいので、それを明示しているわけです。

なぜ80番で待たないのか?という疑問があるかもしれません。ネットワークのお話になってくるので、ここでは参考情報を紹介します。用語としては、「Web3層構造 サーバ」というワードで調べてみてください。
入り口としては、下記記事が良いと思います。

https://qiita.com/yCroma/items/e46476e2ac7c372bb2a3

Controllerの作成

Tomcatへアクセスすると、対応するJavaのプログラムが実行されます。
SpringWebが提供する機能において、ユーザのアクセスを受けて、その処理方法を決めるクラスのことをControllerと呼んでいます。

Controllerの役割は、ユーザの入力を受け取って、プログラムに渡すという変換処理と、その結果をユーザの求める出力にするという変換処理の2つの変換です。

SpringBootは、/helloというパスを受け取ると、そのパスが適合するControllerを選んで処理を実行させます。ですので、まずはこの入口を作ってみましょう。

まず、Controllerを管理するパッケージとしてpresentationを作成します。

次に、Controllerを作成しましょう。

Controllerの実装は次のとおりです。パッケージ名は自分の作ったパッケージ名に合わせるよう注意してください。

package chalkboard.me.bulletinboard.presentation;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
@RequiredArgsConstructor
public class HelloController {

  @GetMapping("/hello") // localhost:8080/helloはここにアクセスが来る
  public String hello(Model model){
    return "hello";
  }
}

通常、Javaにおけるクラスはインスタンスを生成しなければ利用することはできません。
この原則は、SpringFramework/SpringBootにおいても同様です。
しかし、このControllerクラスはどこからもnewされませんが、きちんとその機能を果たしてくれます。

それを知るためにはアノテーションに着目する必要があります。

DI(Dependency Injection: 依存性の注入)コンテナ

@ナントカと記述しているのがアノテーションと呼ばれる仕組みです。
アノテーションそのものは、そのクラスに対して付与するマーカーとして機能し、そのマーカーのついたクラスに対して様々な効果を別のプログラムが及ぼします。

例えば、@Controllerというアノテーションは、SpringFrameworkのDIコンテナへこのクラスを登録する、という作用があります。
このDIコンテナこそ、SpringFrameworkが提供する強力な機能の一つです。

結果から言うと、DIコンテナは、@Controller, @Repository, @Service...など、いくつかのアノテーションでマークされたクラスのインスタンスを予め生成し、保管している箱です。
そして、それらのインスタンスが必要な箇所に適宜分配することによって、逐一newせずともそのインスタンスを利用することができるようになります。

ControllerをDIして使っている箇所はフレームワークに隠蔽されてみえませんが、DIコンテナがあることによって、開発者はただControllerを作りさえすれば良くなっています。

DIコンテナに入れているインスタンスは基本的にはシングルトンであり、使い回されています。
よって、DIされるすべてのオブジェクトは、基本的にスレッドセーフではありません。
ただ、今の段階では説明が難しいので、この言葉だけ覚えてください

「基本的に、DIして使うモノはスレッドセーフではない」

Lombok

@RequiredArgsConstructorはLombokの提供するアノテーションです。
この@RequiredArgsConstructorJavaDocにある通り、次の条件いずれかにハマるフィールドを初期化するコンストラクタを生成します。

  • finalフィールドであること
  • @NonNullアノテーションであること

@NonNullアノテーションもLombokが提供するアノテーションです。nullであることを許容しないことを示し、nullを入れると例外を発生させるようになります。

@GetMapping

SpringWebの提供するController向けのアノテーションです。類似のものに、

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

があります。

それぞれHTTPメソッドに対応したルートマッピング用のアノテーションで、HTTPメソッドと、URLのパスが一致したときにそのControllerが採用されます。

HTTPメソッド

HTTPメソッドは、そのアクセスがどのようなアクションを実行したいのかを示すものです。詳しくは、MDNのドキュメントを参照してみてください。

今回は、/helloというパスで、GETメソッドであれば、SpringBootはHelloControllerhelloメソッドを呼んでくれるはずです。
これは、@GetMappingアノテーションで、helloメソッドに/helloを指定しているからです。メソッドの名前は、実は何も関係ありません。

GETメソッドというのは情報の取得に使われるもので、リンクを踏むような動作はすべてGETメソッドになります。
予約ページなどで、入力フォームから情報を送るような操作はPOSTメソッドです。

Controllerとthymeleafの対応

今回はHTMLを返したいので、ControllerからどのHTMLテンプレートを返すのかを指定する必要があります。

それを担うのは、helloメソッドの返り値です。今は、return "hello";なのでhelloという文字列ですね。
thymeleafのテンプレートは、この場合下記のパスにHTMLテンプレートがあるかを確認します。
/resources/templates/hello.html

thymeleaf用HTMLテンプレートの作成

しかし、今そこには何もありませんので次のように作成する必要があります。

これにて、Hello.htmlを作ることができました。

HTMLの編集

作ったばかりのHTMLにはなんの情報もありません。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
</html>

次のように書き換えましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Hello world</title>
</head>
<body>
Hello world!
</body>
</html>

HTMLにおいて、<head></head>に書かれた情報はブラウザに向けた情報で、ユーザの目に主立って触れるものではありません。
ブラウザ上に直接レンダリングされるのは<body></body>なので、そこを記述する必要があります。

<html lang="ja">のlangは、そのままそのページの自然言語(日本語とか英語とか)を表します。jaは日本語を指します。(今レンダリングされるのは、英語しか無いですけどね・・・)

起動とアクセス

さて、今一度再生ボタンで起動してみましょう。
ちなみに、このとき起動ログにこのようなログが出ているはずです。

Tomcat initialized with port(s): 8080 (http)

これが、サーブレットコンテナであるTomcatの待受ポートですので、アクセスすべきURLが
http://localhost:8080/helloになるわけです。

実際にブラウザからアクセスしてみましょう。

うまく行ったでしょうか?

補足

SpringBootを起動すると下記のようにWARNINGが表示されます。

これは利用しているオープンソースの中で、「リフレクション」と呼ばれる機能を使っている物があるために表示される警告です。
このWARNINGは無害ですので、無視してください。

補足資料

もし、業務ではじめてSpringBootに触れる必要性を迫られているなら、このスライドが有用です。
スレッドセーフではないということは、間違って使うとユーザの個人情報を流出させたりするかもしれませんので気をつけてください。

https://www.slideshare.net/KouheiToki/1spring

今回のPR

雛形作成完了直後

https://github.com/angelica-keiskei/spring-sample/pull/1

thymeleafでHello world完了後

https://github.com/angelica-keiskei/spring-sample/pull/2