メインコンテンツまでスキップ

eclipseで作るSpringアプリケーション開発環境

Sato Taichi

はじめに#

ここ最近は、SUN が Oracle に買収されて Java 自体の開発に関する状況が大きく変わってきている。

安定的に動作することを期待されている Java が他の言語みたいにどんどん変化するって方向に変わりたいようだ。 新しいもの好きな人々にとっては話題が多いのは素晴らしいことだろうが、アプリケーションが確実に動いて欲しいと思ってる人々にとっては困った話だ。

僕は SIer の従業員なんで、安定的に動作するってことが Java や JVM の素晴らしさだと考えている。

とはいえ、大きく変化してるってんなら、ちゃんとキャッチアップしておかないと困ったことになるのは自分だ。

そういう訳で、今回は eclipse を使って Spring Boot のアプリケーションを開発するための環境構築について説明する。

エントリの変更点#

最初にこのエントリを公開した時には、MockMvc と TestRestTemplate を使ってテストを書くように説明していた。 ところが、@making がより良い方法として、WebTestClient を使う方法を教えてくれたのでそちらを使うように書き換えた。

@making に感謝。

開発環境#

Thinkpad X1 Carbon 2016 年モデルに Windows10 をインストールしてある。

ハードウェアスペックは、こうだ。

  • CPU i7 6600U @ 2.6GHz
  • メモリ 16GB
  • ストレージ SAMSUNG NVMe SSD 950 PRO 512GB

少し高級かもしれないけど開発者用のマシンとしては普通だよね。

アプリケーションのインストール#

基本的には Scoop を使ってインストールしていく。Scoop については、以前のエントリを参照して欲しい。

JDK のインストール#

まず、Java 用の Bucket を Scoop に追加する。

scoop bucket add java

Bucket を追加したので、ローカルディスク上にある Bucket の情報を更新する。

scoop update

次に、Bucket に登録されている JDK を調べる。

scoop search jdk

大体こういう感じで出力されるだろう。

'java' bucket:    ojdkbuild (10.0.2-1)    ojdkbuild10 (10.0.2-1)    ojdkbuild8 (1.8.0.181-1)    ojdkbuild9 (9.0.4-1)    openjdk (10.0.2-13)    openjdk10 (10.0.2-13)    openjdk11 (11-25)    openjdk12 (12-5)    openjdk9 (9.0.4)    oraclejdk-lts (8u181-b13)    oraclejdk (10.0.2-13)    oraclejdk10 (10.0.2-13)    oraclejdk11 (11-25)    oraclejdk8-ea (8u192-b02)    oraclejdk8 (8u181-b13)    oraclejdk8u (8u181-b13)

今となっては OpenJDK と OracleJDK に大きな差分は無いので、OpenJDK を使えばいい。

というわけで、OpenJDK をインストールする。

scoop install openjdk

Java のインストールが成功しているか確認するために、バージョン番号を標準出力する。

java -version

インストール作業をした時期によって細かい部分は違うだろうが、大体こういう出力がされる。

openjdk 10.0.2 2018-07-17OpenJDK Runtime Environment 18.3 (build 10.0.2+13)OpenJDK 64-Bit Server VM 18.3 (build 10.0.2+13, mixed mode)

マジでどうでもいい話だが、今は --version でもバージョン情報を得られる。

Gradle のインストール#

Gradle をインストールするのは、プロジェクトを最初に作る人間だけでいい。

何故なら、プロジェクトを作った後は使う Gradle のバージョンを揃えるために Gradle Wrapper という仕組みを使うからだ。 要はプロジェクトディレクトリの直下に gradlew.bat とかある、あれを使うって事。

Scoop で Gradle をインストールする。

scoop install gradle

これで、ソースコード付きの Gradle がローカルインストールされる。 もし、ネットワークの帯域を節約したいなら、実行バイナリだけが含まれている gradle-bin をインストールしてもいい。 その場合、何かトラブルが起きた時には念力デバッグが必要になる。

Gradle のインストールが成功しているか確認するために、バージョン番号を標準出力する。

gradle --version

大体こういう風に出力される。

WARNING: An illegal reflective access operation has occurredWARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/taichi/scoop/apps/gradle/current/lib/groovy-all-2.4.12.jar) to method java.lang.Object.finalize()WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClassWARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operationsWARNING: All illegal access operations will be denied in a future release
------------------------------------------------------------Gradle 4.9------------------------------------------------------------
Build time:   2018-07-16 08:14:03 UTCRevision:     efcf8c1cf533b03c70f394f270f46a174c738efc
Kotlin DSL:   0.18.4Kotlin:       1.2.41Groovy:       2.4.12Ant:          Apache Ant(TM) version 1.9.11 compiled on March 23 2018JVM:          10.0.2 ("Oracle Corporation" 10.0.2+13)OS:           Windows 10 10.0 amd64

Java9 以来発生している警告で、Java10 を使っている限り出力される。Gradle5 になれば出力されなくなるらしい。cf. Upgrade to Groovy 2.4.12 for full Java 9 compatibility

ワークアラウンドとして JAVA_OPTS 環境変数を以下のように設定しておく。

--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED

eclipse のインストール#

次は、eclipse のインストールだ。Scoop を使ってインストールしても良いがチューニングの経過を保存し易いように zip アーカイブをダウンロードして使う。

以下のページから Windows 64-bit のリンクをクリックしてダウンロードする。

Eclipse Installer はマジで素晴らしい仕組みだとは思うが、GUI 操作が多すぎて何度もインストールしたり捨てたりするような使い方には向いていない。

ダウンロードした zip ファイルを適当なディレクトリに展開すればインストールは完了だ。

eclipse のチューニング#

eclipse.ini#

メモリの上限を変更しつつ、class の検証オプションを無効化する。 あまり変なプラグインは使っていないことが前提になる。

-Xmx8g-Xverify:none

自由にプラグインを追加したいなら、 -Xverify:none にはリスクがある。 クラスファイルが想定していないような書き換えが行われることで、そこから派生して情報漏洩したり、知らないうちに DDoS 攻撃に参加させられてしまうかもしれない。

eclipse のデフォルトだと G1GC を使う事になっている。 しかし、メモリが最大 8G 程度のアプリケーションで G1GC を使うのはちょっとオーバーヘッドが大きすぎないか?と思っている。 ただ、CMS やパラレル GC との差分を調べる気力が無いので、そのままにした。

設定のチューニング#

General の Show heap status を on

メモリが不足していたら分かるようにするためだ。バケツボタンを押して GC をトリガーすることもできる。

General > News の Enable automatic new polling を off

General > Startup and Shutdown の 大半を off

General > Workspace の Text file encoding を UTF-8

MS932 のままでコードを書くと、本当に沢山のトラブルに巻き込まれることになる。

Install/Update > Automatic Updates の Automatically find new updates and notify me を off

Java のコーディングを便利にするチューニング#

Java プロジェクトを一つ作る。中身はなんでもいい。以下の設定が終わったら、当該プロジェクトは消す。

Java > Appearance > Type Filtersjava.awtjavax.print を追加

Java > Editor > Content Asist > Advanced で 不要な入力補完機能を off。上と下に二つ同じ項目があるので、要注意。

  • Java Proposals (Task-focused) は Mylyn 系の機能なので明確に不要
  • SWT Template Proposals は SWT 用なので明確に不要
  • Adaptive Template Proposals は 使ってみて邪魔なら後で off にする
  • Code Recommenders 系は、かなり重いので off にすると快適になる

Java > Editor > Save Actions で Perform the selected actions on save を on

コードのフォーマットや import 文の整理などというのは、人間がやることではないのだよ。

Java > Editor > Typing で Automatically insert at current position の Semicolons を on これで、行のどこでセミコロンキーを押しても正しい位置にセミコロンが入力される。

Java > Installed JREs でローカルに複数の JRE や JDK をインストールしているなら登録する。

便利なプラグインの導入#

google-java-format プラグイン#

ソースコードのフォーマットは、どんなルールでやろうが一貫性のあるルールなら好きなようにすればいいが、その作業自体は人間がやることではない。

エディタなり IDE なりに自動的にやってもらうものだ。

eclipse にもフォーマットの機能はあるけど、他の IDE やエディタを使っている人とその内容を共有するのが難しいのが問題だ。 それに eclipse のデフォルト設定だと最近のコア API やライブラリが採用しているスタイルのコードが読み易くにフォーマットされないという問題もある。

IntelliJ みたいな他の IDE と設定を共有できてまともに使えるコードフォーマッタは、僕が知っている限りでは google-java-format くらいしかない。

と言うわけで、google-java-format プラグインを導入しよう。

まずは、リリースページに行って、最新の jar ファイルをダウンロードする。google-java-format-eclipse-plugin_x.x.x.jar みたいなやつがそれだ。

ダウンロードしてきた Jar ファイルを eclipse の dropins フォルダにコピーする。その後、eclipse を再起動するとプラグインがインストールされる。

プラグインがインストールされたら、Java > Code Style > FormatterFormatter Implementationコンボボックスで、 google-java-format を選ぶ。

最後に、eclipse デフォルトのフォーマッタと google-java-format を使った時の見た目がどう変わるのか確認しておこう。

どんなふうになっていると見易いかってのは、個人の主観によるところが大きいとは思うので、みんな見比べてみて欲しい。

eclipse のデフォルト設定#

google-java-format#

ターミナルを起動するプラグイン#

EasyShell というプラグインを eclipse に導入すると、eclipse のプロジェクトビューから直接ターミナルが起動できる。

Help メニューから Eclipse Marketplace を起動して、検索エリアに EasyShell と入力すれば見つけられる。

EasyShell のインストールが終わったら、コンテキストメニューの設定を変更する。

EasyShell > (1) Menu から、一行目を選んだ状態にした後、Edit ボタンをクリック。

Filter に Power と入力すると、その一行下のセレクトボックスに Open - PowerShell (Plugin) と表示される。この状態で OK を押す。

こんな風に変わる。

Run with Command Prompt は使わないので、左側のチェックボックスを外している。

プロジェクトの作成#

Java プロジェクトの作成#

次は、eclipse で Java プロジェクトを作ろう。ここでは、説明のために first-spring-boot とする。 module-info を作るか聞かれるかもしれないが、 Don't Create を選んでいい。

プロジェクトが出来たら、Java プロジェクトのコンテキストメニューから EasyShell > Open PowerShell Here と選び PowerShell を起動する。

起動した PowerShell で、以下のコマンドを実行する。

gradle init

これで、プロジェクトの直下に Gradle 関連のリソースがいくつか作られる。

  • gradle フォルダ
  • build.gradle
  • gradlew
  • gradlew.bat
  • settings.gradle

eclipse 側に戻って、Java プロジェクトのコンテキストメニューから Configure > Add Gradle Nature を選ぶ。 これで、eclipse が当該プロジェクトを Gradle プロジェクトだと認識するようになる。

最後に Java プロジェクトのプロパティで Gradle の設定を変える。

まず、Override workspace settings のチェックボックスを on にする。 次に、Automatic Project Synchronization のチェックボックスを on にする。

何故、こんなややこしい手順で Gradle プロジェクトを作っているのかというと、eclipse の Gradle プラグインが内部に抱え込んでいる Gradle が古いからだ。

最後に、さっき起動した PowerShell で Gradle 標準のソースコードディレクトリを作る。

mkdir -p src/main/java mkdir -p src/main/resources mkdir -p src/test/java mkdir -p src/test/resources

eclipse の外側でディレクトリを作ったので、Java プロジェクトのコンテキストメニューから Refresh を実行する。

次に、src ディレクトリのコンテキストメニューから Build Path > Remove from Build Path を選ぶ。

更に、さっき作ったディレクトリを全部展開する。コントロールキーを押しながら左クリックを四回繰り返して四つのフォルダを選択状態にした上で、 コンテキストメニューから Build Path > Use as Source Folder を選ぶ。

Gradle のビルドスクリプトを初期設定する#

eclipse で build.gradle を開き以下の内容をコピペする。

plugins {    id 'java'}
repositories {    maven {        url 'https://maven-central-asia.storage-download.googleapis.com/repos/central/data/'    }    jcenter()    mavenCentral()}
sourceCompatibility = targetCompatibility = 1.10
tasks.withType(AbstractCompile)*.options*.encoding = 'UTF-8'

repositories ブロックでは、Google が用意している Maven Central リポジトリのミラーを参照するようにしている。 これは、僕がいるネットワーク内だと大抵の場合、Apache が運営しているものよりも高速にライブラリをダウンロードできるからだ。

今回は Java10 で作業しているので、 sourceCompatibility1.10 にしている。別に 1.8 くらいでも全然問題ない。

末尾にかなり奇妙なコードが書いてあるが、これはソースコードのエンコーディングを全て UTF-8 で統一するまじないの一種だと考えてくれればいい。

尚、Gradle 側には eclipse プラグインは特に導入しない。

設定が正しくなされているかを確認するために、PowerShell で以下のコマンドを実行する。

.\gradlew build

ここからは、Gradle Wrapper を使って作業するのでプロジェクトディレクトリの直下にあるシェルスクリプトを使っている。 特に問題が無ければ、以下のように出力される。

BUILD SUCCESSFUL in 1s1 actionable task: 1 up-to-date

JUnit5 を導入する#

次は、JUnit5 を Gradle に導入しよう。

test {    useJUnitPlatform()    systemProperty('java.awt.headless', true)    jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED', '--add-opens=java.base/java.lang.invoke=ALL-UNNAMED'}
dependencies {    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.+'    testRuntimeOnly    'org.junit.jupiter:junit-jupiter-engine:5.2.+'}

JUnit5 を Gradle で使うには、 test ブロックで useJUnitPlatform() を呼ぶ必要がある。

ユニットテストの実行中は、AWT を使う気が無いのでシステムプロパティの java.awt.headless を有効化している。

Gradle のインストール辺りで触れたリフレクション周りの警告が出ないようにするワークアラウンドをここでも追加している。 Gradle5 になれば、このオプションは要らなくなる。

dependencies ブロックでは、JUnit5 のライブラリを二つ追加している。

詳細な使い方や、ライブラリの意味なんかは JUnit 5 User Guide を参照して欲しい。

テストコードの動作確認#

テストコードが上手く動作するか確認しておこう。

src/test/java フォルダに、 FirstTest クラスを作成する。

以下のコードをコピーした後、当該フォルダを選択してペーストすれば eclipse がファイルを作ってくれる。

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;
public class FirstTest {
    String message;
    @BeforeEach    public void setUp() throws Exception {        this.message = "Hello";    }
    @Test    public void hello() throws Exception {        assertEquals("Hello", this.message);    }}

JUnit4 では、@Before だったものが JUnit5 では、 @BeforeEach に変ったので注意しよう。

ちなみに僕は @Before を付けた setUp メソッドが何回やっても実行されずにドハマりした。

まずは、 eclipse でこのテストコードが動かせるか確認しよう。

このコードのコンテキストメニューから、 Run As > JUnit Test と選択する。

上手く動作すればテストビューでグリーンバーが見られる。

次に、Gradle でこのテストコードが動かせるか確認しよう。

.\gradlew test

必ず失敗するようにテストコードを変更して動作確認するのも忘れずに。

Spring Boot を導入する#

さて、本丸の Spring Boot 導入だ。

まずはシンプルに Spring Boot で開発するのに最小限必要な依存性を追加したビルドスクリプトがこれだ。

plugins {    id 'java'    id 'org.springframework.boot' version '2.0.4.RELEASE'    id 'io.spring.dependency-management' version '1.0.6.RELEASE'}
repositories {    maven {        url 'https://maven-central.storage.googleapis.com'    }    jcenter()    mavenCentral()}
sourceCompatibility = targetCompatibility = 1.10
tasks.withType(AbstractCompile)*.options*.encoding = 'UTF-8'
bootJar {    baseName = 'first-spring-boot'    version =  '0.1.0'}
test {    useJUnitPlatform()    systemProperty('java.awt.headless', true)    jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED', '--add-opens=java.base/java.lang.invoke=ALL-UNNAMED'}
dependencies {    implementation 'org.springframework.boot:spring-boot-starter-web'    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.0'    testRuntimeOnly    'org.junit.jupiter:junit-jupiter-engine:5.2.0'}

まず、 Gradle の Spring Boot プラグインを導入している。 plugins ブロックに増えた二行がそれだ。

これによって、ビルドスクリプトの中で bootJar ブロックが使えるようになった。baseNameversion は必須なので適当な値を設定しておく。

最後に依存ライブラリとして、 spring-boot-starter-webspring-boot-starter-test を追加して完成だ。

Spring Boot アプリケーションの作成#

Spring Boot アプリケーションとして動作確認するためのコードを用意する。

以下のコードを src/main/java フォルダに eclipse でコピペすれば、パッケージも込みで自動的に作ってくれる。便利!

package hello;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class Application {
  public static void main(String[] args) {    SpringApplication.run(Application.class, args);  }}
package hello;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
@RestControllerpublic class HelloController {
  @RequestMapping("/")  public String index() {    return "Greetings from Spring Boot!";  }}

Application クラスのコンテキストメニューで、 Run As > Java Application を選択するとサーバが起動する。

Console ビューに出るログが止まった辺りでブラウザを使って http://localhost:8080/ にアクセスしよう。

上手く動作しているなら、

Greetings from Spring Boot!

とブラウザに表示される。

Gradle を使ってアプリケーションを起動するなら、

.\gradlew bootrun

で、同じように実行できる。

Spring Boot アプリケーションをテストする#

次は、テストコードを書いてみよう。

まずは、サーバを使わずにコントローラをテストする。 その前に Spring WebFlux をテスト用の依存性に加えておく。

dependencies {    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'    testImplementation 'org.springframework.boot:spring-boot-starter-webflux'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.0'    testRuntimeOnly    'org.junit.jupiter:junit-jupiter-engine:5.2.0'}

dependencies ブロックだけ抜粋したが、 spring-boot-starter-webflux がそれだ。 Spring WebFlux はリアクティブアプリケーションを構築するための新しい枠組みだが、全てが非同期指向になるため使う場所を選ぶ。 具体的に言うと、JDBC を使うアプリケーションでは適用できない。

今回は、Spring WebFlux の一部である WebTestClient という便利な API をテストのときだけ使う。

こいつは、何が素晴らしいって僕の嫌いな Hamcrest に依存している MockMvc を使わなくて良くなる上に、 ユニットテストとインテグレーションテストを同じ API 体系の中でテストできるのだ。使わない理由はない。

package hello;
import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;import org.springframework.http.MediaType;import org.springframework.test.context.junit.jupiter.SpringExtension;import org.springframework.test.web.reactive.server.WebTestClient;
@ExtendWith(SpringExtension.class)@SpringBootTest(webEnvironment = WebEnvironment.MOCK)public class HelloControllerTest {
  WebTestClient client;
  @Autowired HelloController target;
  @BeforeEach  public void setUp() throws Exception {    this.client = WebTestClient.bindToController(this.target).build();  }
  @Test  public void getHello() throws Exception {    this.client        .get()        .uri("/")        .accept(MediaType.APPLICATION_JSON)        .exchange()        .expectStatus()        .isOk()        .expectBody(String.class)        .isEqualTo("Greetings from Spring Boot!");  }}

スッキリとテストを書けるのが分るだろう。テストの実行方法は既に説明したとおりだ。

次は、サーバを起動してテストするインテグレーションテストだ。

package hello;
import org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit.jupiter.SpringExtension;import org.springframework.test.web.reactive.server.WebTestClient;
@ExtendWith(SpringExtension.class)@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)public class HelloControllerIT {
  @Autowired WebTestClient client;
  @Test  public void getHello() throws Exception {    this.client        .get()        .uri("/")        .exchange()        .expectStatus()        .isOk()        .expectBody(String.class)        .isEqualTo("Greetings from Spring Boot!");  }}

ローカルのサーバに接続して行うインテグレーションテストなら、更にコードは短くなる。

Spring Boot を調整する#

さて、一通りテストまで動いたので Spring Boot アプリケーションは開発できるようになったと言いたい所だが、まだ続きがある。

動作するサーブレットコンテナを変える#

Spring Boot がデフォルトで使うサーブレットコンテナは Tomcat だ。まぁ、猫さんはみんなに人気があるので仕方がない。

しかし、猫さんは動作がモッサリなのでキビキビ動く Undertow にサーブレットコンテナを切り替えよう。

dependencies ブロックだけを抜粋して説明するが、こういう風になる。

dependencies {    implementation ('org.springframework.boot:spring-boot-starter-web') {        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'    }    runtimeOnly 'org.springframework.boot:spring-boot-starter-undertow'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'    testImplementation 'org.springframework.boot:spring-boot-starter-webflux'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.+'    testRuntimeOnly    'org.junit.jupiter:junit-jupiter-engine:5.2.+'}

まず、 spring-boot-starter-web の自動的な依存性から spring-boot-starter-tomcat を取り除く。これで Tomcat が自動的に使われることはなくなる。

次に、spring-boot-starter-undertow を依存性に追加して、Undertow が自動的に使われるようにする。

小さいアプリケーションだと差分は小さいけど、アプリケーションが大きくなると起動時間というのは、生産性に大きく関わってくる部分なので少しでも速くしたいよね。

JUnit4 と Hamcrest を推移的な依存性から排除する#

JUnit4 と JUnit5 は、名前が似たクラスやアノテーションが多く存在している。

JUnit5 を使うと決めたプロジェクトの依存性の中に JUnit4 が含まれていると、クラス名が一意に定まらないせいで eclipse が上手く入力補完してくれないことがある。

Hamcrest については、特に使いやすくもなければ分かり易くもないと考えている。 assertEquals で済む話を static import を駆使して何か英語のように読めなくもないものをコードとして書く代わりに沢山の API を覚えるとか手段と目的が逆転してしまっている。 ユニットテストをするために覚える事は少なければ少ないほどいい。

というわけで、JUnit4 と Hamcrest には感謝を伝えると共に完全排除しよう。

dependencies {    implementation ('org.springframework.boot:spring-boot-starter-web') {        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'    }    runtimeOnly 'org.springframework.boot:spring-boot-starter-undertow'
    testImplementation ('org.springframework.boot:spring-boot-starter-test') {        exclude group: 'junit'        exclude group: 'org.hamcrest'    }    testImplementation 'org.springframework.boot:spring-boot-starter-webflux'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.+'    testRuntimeOnly    'org.junit.jupiter:junit-jupiter-engine:5.2.+'}

と言っても、やることは簡単だ。spring-boot-starter-test の推移的依存性から junitorg.hamcrest を外すだけ。

Springfox (Swagger) を追加する#

現代的なアプリケーションの実装において、API アクセスを全く想定しないってのは色々無理がある。 別に全く知らないどこかの誰かに向けて API を用意しようって話をしたいわけじゃない。

ユーザインターフェースを SPA っぽくしようってだけでも API アクセスは必要だし、 郵便番号から住所を引くみたいなちょっとしたウィジェットを動かすだけでも API アクセスしたくなるものだ。

とはいえ、できる限り少ない手間で API アクセスできるようにしたいし、ドキュメントや API アクセスの実験をする環境は欲しい。

そういう訳でSpringfox (Swagger)の登場だ。

動いたテストコードからのみドキュメントを作るみたいなアプローチなら、Spring REST Docs を使うのも悪くない。

Springfox を導入するのは凄く簡単だ。

ビルドスクリプトの dependencies ブロックに二行追加して、エントリポイントのクラスに設定をちょっと書くだけ。

Springfox 関連のライブラリを追加したdependencies ブロックはこんな感じだ。

dependencies {    implementation ('org.springframework.boot:spring-boot-starter-web') {        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'    }    runtimeOnly 'org.springframework.boot:spring-boot-starter-undertow'
    implementation 'io.springfox:springfox-swagger2:2.9.2'    runtimeClasspath 'io.springfox:springfox-swagger-ui:2.9.2'
    testImplementation ('org.springframework.boot:spring-boot-starter-test') {        exclude group: 'junit'        exclude group: 'org.hamcrest'    }    testImplementation 'org.springframework.boot:spring-boot-starter-webflux'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.+'    testRuntimeOnly    'org.junit.jupiter:junit-jupiter-engine:5.2.+'}

springfox-swagger2 には、ビルド時に必要になるクラスがごっそり入っている。

springfox-swagger-ui は、ブラウザでアクセスできる GUI アプリケーションだ。

ビルドスクリプトを変更したら、次はエントリポイントのクラスだ。こんな感じになる。

package hello;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication@EnableSwagger2public class Application {
  public static void main(String[] args) {    SpringApplication.run(Application.class, args);  }
  @Bean  public Docket api() {    return new Docket(DocumentationType.SWAGGER_2)        .select()        .apis(RequestHandlerSelectors.any())        .paths(PathSelectors.any())        .build();  }}

import 文を除く変更点は二つ。 @EnableSwagger2 をクラスに追加したのと、@Beanのついたapiメソッドを追加したことだ。

この状態でアプリケーションを起動して、http://localhost:8080/swagger-ui.html にアクセスすれば、API のドキュメントやテスト環境を試せる。

ああ……ブラボー……

まとめ#

今回は、JDK のインストールから始めて Spring の開発環境を一通り揃えるところまでを説明した。

最近の Java 開発では、IDE として IntelliJ を使うことが多くなってきているような気がするけども、eclipse もまだまだ便利だ。

ただ、eclipse には動作を鈍重にしてしまうような罠が多い。その最たるものが Web Standard Tools(WST)だ。 サーバを起動する機能とかあって便利そうに見えるけど、こいつを入れた途端、eclipse が全体的に遅くなる。

Spring Tool Suite (STS) も推移的に WST をインストールするので、STS を使うという事は不快な eclipse を使うということに直結する。

Spring Boot ならアプリケーションを main エントリから起動できるので従来の J2EE サーバへのデプロイ的な作業はいらない。 つまり、WST が持っているサーバ管理機能みたいなものはそもそもいらないのだ。

STS で唯一便利な機能は、RequestMapping を一覧する機能だが、これも Springfox を追加してしまえばより見やすいものが得られる。

正直言えば、最近は JavaScript ばかり書いているので、VS Code で Java も書きたいのだけど、VS Code の Java 拡張は 2018 年 8 月現在だと、まだまだまともに使えるレベルに無い。 内部的には eclipse の機能が動作するし、設定ファイルとして eclipse らしさが露出してくるのでむしろ困惑する…みたいな状態だ。

僕としては Java のコードを書くなら eclipse が最高だと思っているんだけど、あなたはどうだろうか?

このエントリで説明しなかったこと#

色々あるんだけども、みんなが余り知らなそうなものや、おススメを列挙しておく。

ライブラリ#

ツール#