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

Last updated on

はじめに

ここ最近は、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-17
OpenJDK 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 occurred
WARNING: 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.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

------------------------------------------------------------
Gradle 4.9
------------------------------------------------------------

Build time:   2018-07-16 08:14:03 UTC
Revision:     efcf8c1cf533b03c70f394f270f46a174c738efc

Kotlin DSL:   0.18.4
Kotlin:       1.2.41
Groovy:       2.4.12
Ant:          Apache Ant(TM) version 1.9.11 compiled on March 23 2018
JVM:          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 1s
1 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;

@SpringBootApplication
public 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;

@RestController
public 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
@EnableSwagger2
public 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が最高だと思っているんだけど、あなたはどうだろうか?

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

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

ライブラリ

ツール