# Spring Framework Learning 03 ## Spring Boot 今回からSpringを使ったWebプログラミングを始めます。始めるにあたって、JavaでのWebプログラミングの仕組みを簡単に説明し、現在のSpring開発を支える重要な仕組みであるSpring Bootについて触れていきたいと思います。 ### 👀 JavaにおけるWebプログラミング Springは、**Webサーバ**を構築するためのフレームワークです。Webサーバとは一体なんでしょうか。まず、サーバとはクライアント(ユーザ)の要求に対して、サービスを提供するものです。単にサーバと言った場合には、サービスを提供するために基本的に年中無休稼働するコンピュータを指す場合が多いです。サーバ(ハードウェア)には、メールを処理するためのメールサーバや正しい時刻を返すNTP(ネットワークタイムプロトコル)サーバなど、様々なソフトウェアサーバが動いています。Webサーバもそのうちの一つです。Webサーバはクライアント(ブラウザやCLIコマンド)の要求に対し、HTML・CSS・JS・画像などを返すのが仕事です。**サーバサイドプログラミング**と言った場合には主にデータベースと連携し、クライアントの情報を保存し、レスポンスとなるデータ(HTMLなど)に付与することが主な目的です。例えばSNSでは、自分だけでなく他人と情報をやり取りするためにデータを蓄積し、中継し、発信するためにWebサーバが必須です。 クライアントとサーバとのやり取り ![](https://i.imgur.com/wtivccI.png) Webサーバを起ち上げる仕組みは、言語によって様々です。Javaではレスポンスを返すプログラム(**サーブレット**)やJavaからHTMLを生成する仕組み(**JSP**、JavaServer Pages)をコンパイルしclassファイルを生成し、その設定を記述したweb.xmlをアーカイブしたwarファイルをTomcatと呼ばれるサーブレットを動かす**サーブレットコンテナ**に配置(**デプロイ**)することで、Webサーバを立ち上げていました。 Tomcatを利用したWebサーバの立ち上げ ![](https://i.imgur.com/Gt7pArg.png) ### 👀 Spring Boot Tomcatを利用したWebサーバの立ち上げを説明しましたが、これはとても大変な作業で、本番の作業だけでなく、単純にWebサーバの挙動を確かめるだけでも、この作業をしければならなくとても大変でした。以前のSpringも同様の作業が必要で、かつ様々な仕組みのために設定ファイル(xml)を大量に書く必要がありました。その大部分を解決しているのがSpring Bootになります。実際にSpring Bootが解決している事柄は、この2つだけではありませんが、詳細の説明については省略させていただきます。 まずWebサーバの立ち上げの煩雑さに関しては、Springの各プロジェクト毎に、Tomcatを内包し、設定を自動化することで解決しています。具体的には、一つのjarファイルにプログラム・ライブラリ・Tomcatなどが内包され、これを実行するだけでサーバを起ち上げることが出来ます。また、Gradleを利用している場合には、Spring Boot専用のコマンド(bootRun)を実行するだけでサーバが立ち上げられます。 ```shell $ java -jar spring.jar # もしくは $ ./gradlew.bat bootRun ``` また、大量のXMLの設定を書かなければ行けない問題は、アノテーションを使用し裏で自動化をすることで解消しています。この自動化を担っているのがSpring Bootになります。 ### 💪 Spring BootによるHello World Spring Bootを有効にするには、**spring-boot-starter**と呼ばれる依存関係とGradleを利用している場合は、SpringのGradleプラグインを追加するだけです。しかし、実際にはWebアプリケーションに必要なライブラリやデータベースを操作するためのライブラリを依存解決する必要があります。この複雑さを解決するために、boot-starterは、様々な組み合わせのライブラリのまとまりを提供しています。よく使われるライブラリのまとまりを以下にまとめます。 - spring-boot-starter-web - web開発に必要な詰め合わせ、tomcat、Spring MVC - spring-boot-starter-devtools - 開発をサポートする機能 - spring-boot-starter-test - springのEnd-to-Endテスト等 - spring-boot-starter-thymeleaf - テンプレートエンジンThymeleaf - spring-boot-starter-data-jpa - データベースを扱うJava ORM これらのライブラリを、逐一Maven Central等から探すのは大変です。Springの公式サイトでは、これらの依存関係を簡単に選択・追加しGradleプロジェクトを自動生成できるサービスを提供しています。 [Spring INITIALIZR](https://start.spring.io) 今回は、以下の構成でGradleプロジェクトのテンプレートを生成してみましょう。 Project: Gradle Language: Java Spring Boot: 1.5.3 Group: org.[学籍番号] Artifact: hello-spring Dependencies: Web, DevTools ![](https://i.imgur.com/w50ZqqI.png) プロジェクトを生成したらzipを解凍し、IDEAでOpenしてみましょう。様々なライブラリをダウンロードするため数分の時間が掛かります。 ```build.gradle```を見てみましょう。先程説明したとおり8行目で、Spring Bootのためのプラグインを依存関係に追加し、14行目で有効にしています。このお陰で、25行目からのspring-boot-starterには、バージョンが明記する必要がありません(SpringBoot Pluginのバージョンのみに依存します)。 build.gradle ```groovy= buildscript { ext { springBootVersion = '1.5.3.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'org.springframework.boot' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-web') runtime('org.springframework.boot:spring-boot-devtools') testCompile('org.springframework.boot:spring-boot-starter-test') } ``` 続いて、```HelloSpringApplication.java```を見てみましょう。このファイルは、変更が必要です。12-15行目までを追記しましょう。 HelloSpringApplication.java ```java= package org.ababup1192.hellospring; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class HelloSpringApplication { @GetMapping("/") public String hello() { return "Hello World!"; } public static void main(String[] args) { SpringApplication.run(HelloSpringApplication.class, args); } } ``` 実行は様々なログを確認したいので、IDEA上からでも出来ますが、ターミナルから実行をしてみましょう。そうすると、ログの中から```tomcatが8080```ポートで立ち上がっていることが確認できるはずです。 ```shell $ ./gradlew.bat bootRun . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.5.3.RELEASE) 2017-05-30 00:00:55.179 INFO 14937 --- [ restartedMain] o.a.hellospring.HelloSpringApplication : Starting HelloSpringApplication on ababnoMacBook-Pro.local with PID 14937 (/Users/abab/Desktop/hello-spring/build/classes/main started by abab in /Users/abab/Desktop/hello-spring) 2017-05-30 00:00:55.179 INFO 14937 --- [ restartedMain] o.a.hellospring.HelloSpringApplication : No active profile set, falling back to default profiles: default 2017-05-30 00:00:55.182 INFO 14937 --- [ restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2e7e866a: startup date [Tue May 30 00:00:55 JST 2017]; root of context hierarchy 2017-05-30 00:00:55.503 INFO 14937 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http) 2017-05-30 00:00:55.505 INFO 14937 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service Tomcat ``` お好みのブラウザで```http://localhost:8080/```にアクセスしてみましょう。以下のように表示されれば成功です。 ![](https://i.imgur.com/lro0UUs.png) もし、既に起動ポート(8080)が使われている場合には、起動に失敗してしまいます。その場合は、```src/main/resource/application.properties```でポートを変えることができます。以下の内容を追記してしましょう。 ```src/main/resource/application.properties``` ``` server.port=8090 ``` また、Devtoolsがインストールされているため、```return "Hello World!";```の文字列を任意の文字列に変更し、IDEAのメニューから```Build -> Build Project```を実行してからブラウザを更新すれば、文字列が変化しているのが確認出来るはずです。試してみましょう。 しかし、ブラウザで動きを確認する方法は、リクエストの種類が増えたときや仕様の変更があったときには、困難です。これを解決するために自動テストを書いてみましょう。以下のように```HelloSpringApplicationTest.java```を書き換えてみましょう。 test/java/org/[student_id]/hellospring/HelloSpringApplicationTests.java ```=java package org.ababup1192.hellospring; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.client.RestTemplate; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class HelloSpringApplicationTests { @Autowired private TestRestTemplate restTemplate; @Test public void testHello() { assertThat(restTemplate.getForObject("/", String.class), is("Hello World!")); } } ``` テストは、以下のように実行できます。テストのレポートは、```build/reports/tests/test/index.html```に生成されています。見てみましょう。 ``` $ ./gradlew test :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources NO-SOURCE :testClasses UP-TO-DATE :test UP-TO-DATE BUILD SUCCESSFUL Total time: 1.132 secs ``` ### 💪 リクエストパラメータの取得 SpringにおけるHello Worldでは、```http:localhost:8080/```にアクセスして結果を受け取っていました。これは、helloメソッドに付与したアノテーションに渡しているパラメータがルートであるためこのような挙動になりました``` @GetMapping("/")```。ユーザが自由な値を受け取る場合には、以下のように書く必要があります。以下の```echo```メソッドを追記してみましょう。 ```java @GetMapping("/echo/{str}") public @ResponseBody String echo(@PathVariable("str") String str) { return str; } ``` サーバを起動し、```http://localhost:8080/echo/hello```にアクセスしてみましょう。成功していれば、```hello```とブラウザに表示されているはずです。これは、```/echo/```に続く文字列のうち、```{}```で囲まれた名前(```str```)は、メソッドの引数```string str```で受け取れることを意味しています。但し、```@PathVariable(変数名)```を引数に付与する必要があります。 ### 💪 演習 - echoに対するテストを追加しましょう。 - 以下のテストコードを追記し、テストコードを通過するようなメソッド(reverse, add, repeat, delimit)を追加せよ。 [テストコード](https://gist.github.com/ababup1192/555e3cc1e304c4e405c66c8486bd5800)