前回までで、Spring Bootを使ったWebアプリケーションのGradleプロジェクトの作成をしました。
今回からは、いよいよSpring BootをつかってWebアプリケーションを構築していきます。
1. 実装する画面のイメージ
まずは手始めに3画面のWebアプリケーションを作成してみます。
初めの画面で名前を入力、次の画面で年齢を入力、最後の画面で入力内容を表示します。
画面遷移のイメージは次のようになります。
2. 画面作成の準備
画面はテンプレートエンジンのThymeleafを使用します。ここではThymeleafの説明は省略しますが、昔は標準だったJSPのようなものと思えばよいです。
Thymeleafを使用するため、プロジェクトの依存関係にThymeleafを追加する必要があります。
前回、編集したbuild.gradleファイルのdependenciesに依存関係を追加します。Spring Bootで用意されている「spring-boot-starter-thymeleaf」を依存関係に追加しましょう。
dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' testImplementation 'org.springframework.boot:spring-boot-starter-test' }
build.gradleを更新したら、更新内容をプロジェクトに反映する必要があります。プロジェクトのトップ、もしくはbuild.gradleの上で右クリックして、[Gradle] - [Gradleプロジェクトのリフレッシュ]をクリックします。
この操作は、今後、何度も出てくるので覚えておきましょう。
3. 画面の作成
Thymeleafは画面をHTMLで作成するので、HTMLを書くスキルさえあれば画面を作成できます。
テンプレートファイルはプロジェクトの「src/main/resources/templates」に配置します。
名前入力画面は次のような実装になります。「src/main/resources/templates」に「name.html」として作成します。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Name</title> </head> <body> <form th:action="@{/profile/age}" method="post"> <p> <label for="name">名前を入力してください</label> <input type="text" name="name" id="name"> </p> <p> <input type="submit" value="次へ"> </p> </form> </body> </html>
どうでしょう。ほとんどHTMLとの違いを感じないと思います。実際、このファイルをブラウザで直接開いても、レイアウトが崩れることなく表示されます。JSPでは特殊なタグを使用するので、ブラウザでレイアウトを確認できませんでしたが、Thymeleafだとレイアウトを確認しながら作成することができます。
では、通常のHTMLと違う部分を解説します。
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
htmlタグに「xmlns:th」属性の指定があります。通常のHTMLではない属性です。これはThymeleaf独自の属性を使用するための準備で、「th」という名前を定義しています。
<form th:action="@{/profile/age}" method="post">
formの属性にも違いがあります。通常は入力値をサーバに送信するときのURLを定義する「action」属性を指定しますが、「th:action」属性になっています。
通常のaction属性はURLのホスト名以降のパスを指定します。例えば、フォームの送信先URLが「hhtp://localhost/penguin-web/profile/age」の場合、「/penguin-web/profile/age」を指定します。
しかし、通常のWebアプリケーションではURLのパスの第1要素にあたるコンテキストルートは、実装中に含めないようにすべきです。コンテキストルートは設定で簡単に切り替えられるので、実装中にコンテキストルートを記述してしまうと、コンテキストルートの設定を変更すると、実装側も修正する必要が出てくるためです。
Thymeleafの属性である「th:action」属性に「@{コンテキストルートを除いたパス}」を指定することで、実行時にコンテキストルートが自動で追加されます。
年齢入力画面は次のような実装になります。「src/main/resources/templates」に「age.html」として作成します。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Age</title> </head> <body> <form th:action="@{/profile/hello}" method="post"> <p> <label for="age"><span th:text="${name}">あなた</span>さんの年齢を入力してください</label> <input type="text" name="age" id="age"> </p> <p> <input type="submit" value="次へ"> </p> </form> </body> </html>
名前入力画面の実装とほとんど同じですが、一か所違いがあります。
最初の画面イメージにあるとおり、年齢入力画面には、一つ前の名前入力画面で入力された名前を表示します。
実装している箇所は次の部分です。
<label for="age"><span th:text="${name}">あなた</span>さんの年齢を入力してください</label>
これをブラウザで表示すると、「あなたさんの年齢を入力してください」と表示されます。
ポイントは、「あなた」を括っているspan要素の「th:text」属性です。「th:text」属性の値として、「${name}」が指定されています。これは、span要素のテキストをサーバサイドで設定した「name」の値に置き換えることを意味します。
サーバの実装で説明しますが、この実装によって、span属性のテキスト「あなた」部分が名前入力画面で入力された値に置き換えられます。
最後に、ようこそ画面は次のような実装になります。「src/main/resources/templates」に「hello.html」として作成します。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Hello</title> </head> <body> <p>ようこそ<span th:text="${name}">あなた</span>さん</p> <p><span th:text="${age}">20</span>歳なんですね。</p> </body> </html>
年齢入力画面と同様、名前と年齢を表示する部分にspan要素を配置し、「th:text」属性で、サーバサイドで設定した名前と年齢が表示されるようにしています。
4. コントローラークラスの作成
画面の作成が終わったので、次にWebアプリケーションの処理部分を作成します。
Spring Frameworkでは、画面にまつわる処理はコントローラーと呼ばれるクラスで実装します。
今回のコントローラーは以下のようになります。細かな説明は後程します。
src/main/javaに「penguin.web.controller」パッケージを作成して、「ProfileController」クラスを作成します。
package penguin.web.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @Controller @RequestMapping("profile") public class ProfileController { @RequestMapping("name") public String name() { return "name"; } @RequestMapping(path = "age", method = RequestMethod.POST) public String age(@RequestParam("name") String name, Model model) { model.addAttribute("name", name); return "age"; } @RequestMapping(path = "hello", method = RequestMethod.POST) public String hello(@RequestParam("age") String age, Model model) { model.addAttribute("age", age); return "hello"; } }
クラス定義部
@Controller @RequestMapping("profile") public class ProfileController {
クラスに2つのアノテーションが指定されています。
1つめは「@Controller」アノテーションです。このクラスがコントローラークラスであることをSpring Frameworkに伝えるためのもので、「@Controller」アノテーションの指定は必須です。
2つめは「@RequestMapping」アノテーションです。属性に「profile」という文字列が指定されています。Spring FrameworkのWebアプリケーションは、URLのパス部分をコントローラーで指定します。ここで「@RequestMapping」アノテーションに指定した「profile」はURLの一部になります。URLの全体がどうなるかは後で説明します。「@RequestMapping」アノテーションの指定は必須ではありません。
メソッド定義部
@RequestMapping("name") public String name() { return "name"; }
まずは、一つ目の「name」メソッドです。
このメソッドの役割は名前入力画面を表示することです。
メソッドに「@RequestMapping」アノテーションが指定されています。属性に「name」が指定されています。これはクラスに指定されていた「@RequestMapping」と同様、URLの一部になります。
そして、「name」という文字列を復帰値としています。コントローラクラスの復帰値はいろんな意味がありますが、ここではThymeleafのテンプレートファイルのファイル名を表します。テンプレートファイルは「src/main/resources/templates」フォルダに作成したことを思い出してください。どのテンプレートファイルがブラウザに表示されるかは、以下の内容で決定されます。
"src/main/resources/templates/" + コントローラーメソッドの復帰値 + ".html"
このメソッドは「name」を復帰値としているので、ブラウザには「src/main/resources/templates/name.html」の内容が表示されることになります。
@RequestMapping(path = "age", method = RequestMethod.POST) public String age(@RequestParam("name") String name, Model model) { model.addAttribute("name", name); return "age"; }
次は、二つ目の「age」メソッドです。
このメソッドの役割は名前入力画面で入力された名前を受け取り、年齢入力画面を表示することです。
@RequestMappingに2つの属性が指定されています。1つ目のpath属性は属性名を省略したときと同じ意味で、URLの一部を指定する属性です。
2つ目のmethod属性は受け付けるHTTPメソッドになります。ここでは「RequestMethod.POST」を指定しているので、POSTメソッドのリクエストのみ受け付けることになります。
メソッドには2つの引数があります。第1引数には@RequestParamアノテーションが指定されています。そしてアノテーションの属性に「name」とあります。この@RequestParamアノテーションは、画面から送信されたフォームデータを受け取るためのもので、受け取るフォームデータの名前を属性に指定します。
名前入力画面の以下の箇所で入力された値が、この引数に設定されることになります。
<input type="text" name="name" id="name">
input要素のname属性の値と、コントローラーメソッドの引数の@RequestParamアノテーションの属性を一致させることで、入力内容を受け取れます。
第2引数のModelは、次の画面に渡すデータを設定するためのオブジェクトです。メソッド内の実装でModelオブジェクトが使用されています。
model.addAttribute("name", name);
Modelオブジェクトに「name」という名前で、名前入力画面で入力された値を追加しています。これにより、遷移先の年齢入力画面で「name」という名前を使って、名前入力画面で入力された値を参照することができます。
名前入力画面の次の一文が、この「name」の値を参照している部分です。
<label for="age"><span th:text="${name}">あなた</span>さんの年齢を入力してください</label>
span要素の「th:text」属性に指定している「${name}」が、Modelに設定した「name」を参照していることになります。
そして、最後の「hello」メソッドです。
このメソッドの役割は年齢入力画面で入力された年齢を受け取り、ようこそ画面を表示することです。
@RequestMapping(path = "hello", method = RequestMethod.POST) public String hello(@RequestParam("age") String age, Model model) { model.addAttribute("age", age); return "hello"; }
実装内容は、ageメソッドと同等です。
5. ここまでのまとめ
一気に実装を説明したので、いったん、ここまでのコントローラークラスの実装と画面の関係をまとめます。
名前入力画面と年齢入力画面の関係は次のようになります。
6. Spring Bootアプリケーションの設定
ここまでで、Webアプリケーションとしての実装は完了ですが、Spring Bootアプリケーションにするための設定が必要です。
あと少しです。頑張りましょう!!
Spring Boot Applicationクラスの作成
Spring Bootのアプリケーションとして認識されるために、@SpringBootApplicationアノテーションを指定したクラスと、アプリケーションの起動ポイントとなるmainメソッドが必要となります。
penguin.webパッケージに以下の内容の「Application」クラスを作成します。
package penguin.web; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
コンテキストルートの設定
Spring Bootで作るWebアプリケーション② - Gradleプロジェクトの編集 - ぺんぎんらぼで、Webアプリケーションのコンテキストルートを設定しましたが、実は、この設定はSpring BootのWebアプリケーションでは有効になりません。
Spring Bootでは、application.propertiesファイルにコンテキストルートの設定を記述する必要があります。
src/main/resourcesにプロパティファイル「application.properties」を作成して、以下の内容を記述します。
server.servlet.context-path=/penguin-web
7. さあ、いよいよ実行です!!
おつかれさまでした!! これで、やっとSpring BootのWebアプリケーションとして実行するための実装が完了です。
いよいよ実行ですが、その前に、プロジェクトの構成が以下の通りになっているか、最終確認しましょう。
今回、作成したファイルは以下のものです。
src/main/java
格納先 | ファイル名 | 内容 |
---|---|---|
penguin.web | Applicationクラス | Spring Boot Applicationクラス |
penguin.web.controller | ProfileControllerクラス | 今回作成した画面のコントローラークラス |
src/main/resources
格納先 | ファイル名 | 内容 |
---|---|---|
/templates | name.html | 名前入力画面 |
/templates | age.html | 年齢入力画面 |
/templates | hello.html | ようこそ画面 |
/ | application.properties | Spring Boot設定ファイル |
問題がなければWebアプリケーションを実行します。実行方法は簡単で、作成したApplicationクラスのmainメソッドを実行するだけです。
実行すると、コンソールに次のような起動ログが表示されます。
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.4.RELEASE) 2020-02-23 23:42:04.846 INFO 832 --- [ main] penguin.web.Application : Starting Application on DESKTOP-I4DAKV1 with PID 832 (C:\workspace\penguin-web\bin\main started by Administrator in C:\workspace\penguin-web) 2020-02-23 23:42:04.862 INFO 832 --- [ main] penguin.web.Application : No active profile set, falling back to default profiles: default 2020-02-23 23:42:08.518 INFO 832 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-02-23 23:42:08.543 INFO 832 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-02-23 23:42:08.559 INFO 832 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.30] 2020-02-23 23:42:08.856 INFO 832 --- [ main] o.a.c.c.C.[.[localhost].[/penguin-web] : Initializing Spring embedded WebApplicationContext 2020-02-23 23:42:08.856 INFO 832 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 3807 ms 2020-02-23 23:42:09.465 INFO 832 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-02-23 23:42:10.293 INFO 832 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '/penguin-web' 2020-02-23 23:42:10.302 INFO 832 --- [ main] penguin.web.Application : Started Application in 6.887 seconds (JVM running for 9.477)
アプリケーションが起動したので、Webブラウザからアクセスしてみます。アクセスするURLは以下です。
http://localhost:8080/penguin-web/profile/name
このURLは、application.propertiesに設定したコンテキストルートと、コントローラークラスの@RequestMappingアノテーションで指定した値から決定されます。
アクセスすると、名前入力画面が表示されるので、入力欄に「田中一郎」と入力します。
名前入力画面で「次へ」ボタンをクリックと、年齢入力画面が表示されるので、入力欄に「25」と入力します。
年齢入力画面で「次へ」ボタンをクリックと、ようこそ画面が表示されます。
ようこそ画面をよく見てください。入力した年齢は正しく「25歳なんですね。」と表示されています。
しかし、入力した名前は表示されず「ようこそさん」と表示されています。「ようこそ田中一郎さん」と表示されることを期待した実装です。
なぜ、ようこそ画面に名前が表示されなかったのか。どのようにすれば、ようこそ画面に名前が表示されるかは、次回、説明します。
8. 次回予告
いよいよ、Webアプリの実装が始まりました。
今回は初回であることもあり、かなり詳しく説明したので、かなりのボリュームになりました。
皆さんは実装してみてどのように感じたでしょう?「意外と簡単」と感じた人もいれば、「思ってたより、いろいろと実装しないといけない」と感じた人もいるでしょう。ただ、実装した内容をしっかりと理解すると「意外と簡単!!」と思えるものです。まずは実装して動かす、そして内容を理解することが大切です。
では、次回の「Spring Bootで作るWebアプリケーション④ - 画面間のデータの受け渡し」でお会いしましょう!!