かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Gradle で Multi-project を作成する ( その5 )( lib+webappx2編、Spring Boot ベースの Web アプリケーション(スタブ)のプロジェクトを作成する )

概要

記事一覧はこちらです。

Gradle で Multi-project を作成する ( その4 )( lib+webappx2編、Multi-project の設定ファイルと Spring Boot ベースの Web アプリケーション(メイン)のプロジェクトを作成する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Gradle で Spring を使用しないライブラリ+Spring Boot ベースの Web アプリケーション x 2(メインとスタブ)の Multi-project を作成します。
    • sample-stubapp プロジェクト(こちらが sample-lib の StrNumUtils クラスを呼び出します)を作成した後、sample-webapp の SampleController クラスを本実装して Multi-project を完成させます。

参照したサイト・書籍

  1. Spring @WebMvcTest with Spock Framework
    https://allegro.tech/2018/04/Spring-WebMvcTest-with-Spock.html

  2. Spring Boot RestTemplate POST JSON Example
    https://howtodoinjava.com/spring-boot2/resttemplate-post-json-example/

目次

  1. Spring Boot ベースの Web アプリケーション(スタブ)のプロジェクトを作成する
    1. IntelliJ IDEA で sample-stubapp プロジェクトを作成する
    2. sample-stubapp プロジェクトから不要なファイルを削除する
    3. sample-stubapp プロジェクトの build.gradle を変更する
    4. gradle-multiprj-lib-webapp2 プロジェクトの build.gradle を変更する
    5. settings.gradle に sample-stubapp プロジェクトの include 文を追加する
    6. application.properties に server.port=9080 を追加する
    7. StubWebapiController クラスを新規作成する
  2. sample-webapp の SampleController クラスを本実装する
  3. clean タスク実行 → Rebuild Project 実行 → build タスク実行を行う
  4. Run Dashboard から sample-webapp、sample-stubapp を起動して動作確認する
  5. sample-webapp、sample-stubapp を jar ファイルから起動して動作確認する
  6. まとめ

手順

Spring Boot ベースの Web アプリケーション(スタブ)のプロジェクトを作成する

IntelliJ IDEA で sample-stubapp プロジェクトを作成する

IntelliJ IDEA から Spring Initializr を利用して Spring Boot ベースの Web アプリケーションのプロジェクトを作成します。

f:id:ksby:20190421192134p:plain f:id:ksby:20190421192259p:plain f:id:ksby:20190421192417p:plain f:id:ksby:20190421192450p:plain ※DevTools と Web の2つをチェックします。

f:id:ksby:20190421192551p:plain f:id:ksby:20190421192629p:plain

作成後 IntelliJ IDEA のウィンドウが開きますが、何もせずに閉じます。

sample-stubapp プロジェクトから不要なファイルを削除する

D:\project-springboot\ksbysample-boot-miscellaneous\gradle-multiprj-lib-webapp2\sample-stubapp\ の下には以下のディレクトリ・ファイルがありますが、

f:id:ksby:20190421192923p:plain

src ディレクトリと build.gradle 以外を削除します。削除すると以下の状態になります。

f:id:ksby:20190421193047p:plain

sample-stubapp プロジェクトの build.gradle を変更する

sample-stubapp プロジェクトの build.gradle を以下の内容に変更します。必要な設定は gradle-multiprj-lib-webapp2 の build.gradle に記述したので、このファイルには sample-lib への依存関係だけ記述します。

dependencies {
    implementation project(":sample-lib")
}

gradle-multiprj-lib-webapp2 プロジェクトの build.gradle を変更する

gradle-multiprj-lib-webapp2 プロジェクトの build.gradle を以下のように変更します。

..........

configure(subprojects.findAll { it.name ==~ /^(sample-webapp|sample-stubapp)$/ }) {
    ..........
}
  • it.name ==~ /^(sample-webapp)$/it.name ==~ /^(sample-webapp|sample-stubapp)$/ に変更します。

settings.gradle に sample-stubapp プロジェクトの include 文を追加する

D:\project-springboot\ksbysample-boot-miscellaneous\gradle-multiprj-lib-webapp2 の下の settings.gradle に include 'sample-stubapp' を追加します。

rootProject.name = 'gradle-multiprj-lib-webapp2'
include 'sample-lib'
include 'sample-webapp'
include 'sample-stubapp'

追加後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新すると sample-stubapp が追加されます。

f:id:ksby:20190421194121p:plain

また IntelliJ IDEA の画面右下に「Run Dashboard」のダイアログが表示されます。「Show run configurations Run Dashboard」リンクをクリックすると Run Dashboard Tool Window が表示されます。

f:id:ksby:20190421202534p:plain f:id:ksby:20190421202743p:plain

※ちなみに Run Dashboard Tool Window は Run/Debug Configurations で Spring Boot に2つ以上設定を追加すれば表示させることができます。

f:id:ksby:20190421203153p:plain

clean タスク実行 → Rebuild Project 実行 → build タスク実行を行うと、BUILD SUCCESSFUL のメッセージが出力されました。

f:id:ksby:20190421204259p:plain f:id:ksby:20190421204421p:plain

Project Tool Window を見ると以下のディレクトリ構成になっています。sample-stubapp/build/libs の下に sample-stubapp-1.0.0-RELEASE.jar が生成されています。

f:id:ksby:20190421204810p:plain

application.properties に server.port=9080 を追加する

sample-stubapp/src/main/resources/application.properties に server.port=9080 を追加します。

server.port=9080

StubWebapiController クラスを新規作成する

以下の仕様の StubWebapiController クラスを作成します。

  • URL は /plus にする。
  • HTTP メソッドは POST のみ有効にする。
  • データは JSON で渡す。{ "v1": "<数値文字列>", "v2": "<数値文字列>" } のフォーマットする。
  • レスポンスは { "result": "<数値文字列>" } のフォーマットで返す。
  • エラー処理は考慮しない。

sample-stubapp/src/main/java/ksbysample/webapp/samplestubapp の下に PlusForm、PlusResponse、StubWebapiController クラスを新規作成し、以下の内容を記述します。

■PlusForm

package ksbysample.webapp.samplestubapp;

public class PlusForm {

    private String v1;

    private String v2;

    public String getV1() {
        return v1;
    }

    public void setV1(String v1) {
        this.v1 = v1;
    }

    public String getV2() {
        return v2;
    }

    public void setV2(String v2) {
        this.v2 = v2;
    }

}

■PlusResponse

package ksbysample.webapp.samplestubapp;

public class PlusResponse {

    private String result;

    PlusResponse() {
    }

    PlusResponse(String result) {
        this.result = result;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

}

■StubWebapiController

package ksbysample.webapp.samplestubapp;

import ksbysample.lib.samplelib.StrNumUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StubWebapiController {

    @PostMapping("/plus")
    public PlusResponse plus(@RequestBody PlusForm plusForm) {
        String result = StrNumUtils.plus(plusForm.getV1(), plusForm.getV2());
        PlusResponse plusResponse = new PlusResponse(result);
        return plusResponse;
    }

}

テストを作成します。今回は Spock で作成します。

f:id:ksby:20190421210227p:plain f:id:ksby:20190421210259p:plain

sample-stubapp/src/test/groovy/ksbysample/webapp/samplestubapp/StubWebapiControllerTest.groovy が新規作成されますので、以下の内容を記述します。

package ksbysample.webapp.samplestubapp

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import spock.lang.Specification
import spock.lang.Unroll

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*

@SpringBootTest
@AutoConfigureMockMvc
class StubWebapiControllerTest extends Specification {

    @Autowired
    private MockMvc mvc

    @Unroll
    def "plus WebAPI のテスト(#v1, #v2 --> #result)"() {
        expect:
        mvc.perform(post("/plus")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content("""
                                    {
                                      "v1": ${v1},
                                      "v2": ${v2}
                                    }
                                """))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
                .andExpect(jsonPath('$.result').value(result))

        where:
        v1    | v2  || result
        "1"   | "2" || "3"
        "999" | "1" || "1000"
    }

}

テストを実行すると成功しました。画面左側で WARNING が出ているのは IntelliJ IDEA の JUnit の Run/Debug Configuration に JDK 11 のオプションを 設定していないためです。

f:id:ksby:20190421221830p:plain

sample-stubapp/src/test/java/ksbysample/webapp/samplestubapp/SampleStubappApplicationTests.java は不要なので削除します。

sample-webapp の SampleController クラスを本実装する

sample-webapp/src/main/java/ksbysample/webapp/samplewebapp/SampleWebappApplication.java を以下のように変更します。

package ksbysample.webapp.samplewebapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class SampleWebappApplication {

    private final RestTemplateBuilder restTemplateBuilder;

    public SampleWebappApplication(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplateBuilder = restTemplateBuilder;
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleWebappApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate() {
        return this.restTemplateBuilder
                .rootUri("http://localhost:9080")
                .build();
    }

}
  • private final RestTemplateBuilder restTemplateBuilder; とコンストラクタインジェクションの処理を追加します。
  • @Bean public RestTemplate restTemplate() { ... } を追加します。

sample-stubapp/src/main/java/ksbysample/webapp/samplestubapp の下の PlusForm、PlusResponse クラスを sample-webapp/src/main/java/ksbysample/webapp/samplewebapp の下にコピーします。

sample-webapp/src/main/java/ksbysample.webapp.samplewebapp.SampleController.java を以下の内容に変更します。

package ksbysample.webapp.samplewebapp;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

@Controller
@RequestMapping("/sample")
public class SampleController {

    private final RestTemplate restTemplate;

    public SampleController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @RequestMapping
    @ResponseBody
    public String index() {
        PlusForm plusForm = new PlusForm();
        plusForm.setV1("1");
        plusForm.setV2("2");

        HttpHeaders httpHeaders = new HttpHeaders();
        HttpEntity<PlusForm> request = new HttpEntity<>(plusForm, httpHeaders);

        ResponseEntity<PlusResponse> response =
                restTemplate.postForEntity("/plus", request, PlusResponse.class);

        return response.getBody().getResult();
    }

}

テストを作成します。こちらも Spock で作成します。

f:id:ksby:20190421233800p:plain f:id:ksby:20190421233830p:plain

sample-webapp/src/test/groovy/ksbysample/webapp/samplewebapp/SampleControllerTest.groovy が新規作成されますので、以下の内容を記述します。

package ksbysample.webapp.samplewebapp

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpMethod
import org.springframework.http.MediaType
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.web.client.MockRestServiceServer
import org.springframework.test.web.servlet.MockMvc
import org.springframework.web.client.RestTemplate
import spock.lang.Specification

import static org.springframework.test.web.client.match.MockRestRequestMatchers.method
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@SpringBootTest
@AutoConfigureMockMvc
@DirtiesContext
class SampleControllerTest extends Specification {

    @Autowired
    private MockMvc mvc

    @Autowired
    RestTemplate restTemplate

    def "/sample にアクセスすると 3 と表示される"() {
        setup:
        MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build()
        mockServer.expect(requestTo("http://localhost:9080/plus"))
                .andExpect(method(HttpMethod.POST))
                .andRespond(withSuccess('{"result": "3"}', MediaType.APPLICATION_JSON_UTF8))

        expect:
        mvc.perform(get("/sample"))
                .andExpect(status().isOk())
                .andExpect(content().string("3"))
    }

}

テストを実行すると成功しました。

f:id:ksby:20190422000502p:plain

sample-webapp/src/test/java/ksbysample/webapp/samplewebapp/SampleWebappApplicationTests.java は不要なので削除します。

clean タスク実行 → Rebuild Project 実行 → build タスク実行を行う

全ての実装が完了したので build タスクが正常に終了するか確認します。clean タスク実行 → Rebuild Project 実行 → build タスク実行を行うと、BUILD SUCCESSFUL のメッセージが出力されました。

f:id:ksby:20190422001946p:plain f:id:ksby:20190422002051p:plain

Run Dashboard から sample-webapp、sample-stubapp を起動して動作確認する

Run Dashboard から sample-webapp、sample-stubapp を起動します。Run Dashboard から起動すると使用されているポート番号が表示されるのが分かりやすくていいですね。

f:id:ksby:20190422002338p:plain f:id:ksby:20190422002450p:plain

ブラウザから http://localhost:8080/sample にアクセスすると画面上に "3" の文字が表示されました。

f:id:ksby:20190422002645p:plain

確認後、sample-webapp、sample-stubapp を停止します。

sample-webapp、sample-stubapp を jar ファイルから起動して動作確認する

今度は生成された jar ファイルから起動して確認してみます。コマンドプロンプトから jar ファイルが生成されている build/libs ディレクトリに移動した後、java -jar sample-webapp-1.0.0-RELEASE.jarjava -jar sample-stubapp-1.0.0-RELEASE.jar コマンドを実行します。

f:id:ksby:20190422003050p:plain

ブラウザから http://localhost:8080/sample にアクセスすると画面上に "3" の文字が表示されました。

f:id:ksby:20190422003131p:plain

確認後、sample-webapp、sample-stubapp を停止します。

まとめ

  • サブプロジェクトに Spring Boot ベースの Web アプリケーションを2つ以上作成する場合、共通の設定はプロジェクトのルートディレクトリの build.gradle にまとめられます。
  • Spring Boot ベースのサブプロジェクトと Spring Boot ベースではないサブプロジェクトが混在していて、Spring Boot ベースのサブプロジェクトの設定だけを プロジェクトのルートディレクトリの build.gradle にまとめる場合には configure(subprojects.findAll { it.name ==~ /^(sample-webapp|sample-stubapp)$/ }) { ... } のように記述して設定を適用するサブプロジェクトを指定できます。
  • IntelliJ IDEA の Run Dashboard を表示させる方法がよく分かっていなかったのですが、Run/Debug Configurations で Spring Boot の下に2つ以上設定を追加すれば自動で現れることが分かりました。
  • 簡単なサンプルを書いていたつもりでしたが、テストまで書くと意外に辛いですね。。。 書き方が分からなくて調べながらやっていて結構時間がかかりました。

履歴

2019/04/22
初版発行。