Help us understand the problem. What is going on with this article?

GradleでSpring Bootアプリケーションを実装する

Spring BootをGradleでビルドし、実行するまでの手順。

手順

  1. build.gradleに追記
  2. Spring Boot実行用クラスを作成
  3. API用クラスを作成
  4. GradleでSpring Bootを起動
  5. APIの実行結果を確認

build.gradleに追記

build.gradle
def applicationVersion = project.properties['release.version']

// ~~~

// Spring Boot
buildscript {
    def springBootVersion = '2.0.3.RELEASE'
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:' + springBootVersion
    }
}

apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

def springBootApplicationName = 'hello-world-spring-boot'
bootJar {
    baseName = springBootApplicationName
    version = applicationVersion
}

bootWar {
    baseName = springBootApplicationName
    version = applicationVersion
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.springframework.boot:spring-boot-devtools'
    testCompile 'org.springframework.boot:spring-boot-starter-test'
}

build.gradle全体

build.gradle
// リリースバージョンをgradle.propertiesから取得し設定
def applicationVersion = project.properties['release.version']
version = applicationVersion

// Java バージョン
apply plugin: 'java'
def javaVersion = JavaVersion.VERSION_1_10
sourceCompatibility = javaVersion
targetCompatibility = javaVersion

// warファイルを出力する
apply plugin: 'war'

// 依存ライブラリバージョン
def junitVersion = '5.2.0'
def jacocoVersion = '0.8.1'
def checkstyleVersion = '8.10.1'

// 依存ライブラリ取得先
repositories {
    // Mavenセントラルを使う
    mavenCentral()
}

// 依存ライブラリ
dependencies {
    // JUnit
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: junitVersion
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: junitVersion
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: junitVersion
}

test {
    // gradle buildの時にJUnit5を使うようにする
    useJUnitPlatform()
    // テストの並列実行スレッド数
    maxParallelForks = 4
}

// 各種IDEの設定ファイルを出力するGradleプラグインの設定
apply plugin: 'eclipse'
apply plugin: 'idea'

// テストカバレッジにJacocoを使用する
apply plugin: 'jacoco'
jacoco {
    toolVersion = jacocoVersion
}

jacocoTestReport {
    reports {
        xml.enabled = true
        html.enabled = true
    }
}

build.dependsOn jacocoTestReport

// Checkstyleで静的コード解析をする
apply plugin: 'checkstyle'
checkstyle {
    toolVersion = checkstyleVersion
    // デフォルトでは/src/main/resources配下だが、リポジトリ直下のXMLファイルを参照するようにする
    configFile = file('checkstyle.xml')
    // Checkstyle実行時にエラーがあればビルドを中断するようにする
    ignoreFailures = false
}

// Spring Boot
buildscript {
    def springBootVersion = '2.0.3.RELEASE'
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:' + springBootVersion
    }
}

apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

def springBootApplicationName = 'hello-world-spring-boot'
bootJar {
    baseName = springBootApplicationName
    version = applicationVersion
}

bootWar {
    baseName = springBootApplicationName
    version = applicationVersion
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.springframework.boot:spring-boot-devtools'
    testCompile 'org.springframework.boot:spring-boot-starter-test'
}

Spring Boot実行用クラスを作成

プロダクトコード

Application.java
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);
  }

}

テストコード

ApplicationTest.java
package hello;

import org.junit.jupiter.api.Test;

class ApplicationTest {

  @Test
  void main() {
    Application.main(new String[0]);
  }

}

Spring Bootを自動終了した警告が出るが、問題はない。何かもっと良い解決策があればアドバイスください。

API用クラスを作成

プロダクトコード

HelloWorldController.java
package hello;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;

import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
class HelloWorldController {

  static final String HELLO_WORLD = "Hello, world.";
  private static final int BUFFER_SIZE = 1024;

  @RequestMapping(value = "/string", method = RequestMethod.GET)
  String byString() {
    return HELLO_WORLD;
  }

  @RequestMapping(value = "/byte", method = RequestMethod.GET)
  byte[] byBytes() {
    return HELLO_WORLD.getBytes();
  }

  @RequestMapping(value = "/stream", method = RequestMethod.GET)
  ResponseEntity<InputStreamResource> byStream() {
    final InputStreamResource inputStreamResource = new InputStreamResource(
        new BufferedInputStream(new ByteArrayInputStream(HELLO_WORLD.getBytes()), BUFFER_SIZE)
    );

    final HttpHeaders headers = new HttpHeaders();
    headers.setContentLength(HELLO_WORLD.getBytes().length);

    return new ResponseEntity<>(inputStreamResource, headers, HttpStatus.OK);
  }

}

テストコード

HelloControllerTest.java
package hello;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

class HelloControllerTest {

  @Test
  void byString() {
    final HelloWorldController helloController = new HelloWorldController();
    Assertions.assertAll(
        () -> Assertions.assertEquals(helloController.byString(), HelloWorldController.HELLO_WORLD)
    );
  }

  @Test
  void byBytes() {
    final HelloWorldController helloController = new HelloWorldController();
    Assertions.assertAll(
        () -> Assertions.assertEquals(Arrays.toString(helloController.byBytes()),
            Arrays.toString(HelloWorldController.HELLO_WORLD.getBytes()))
    );
  }

  @Test
  void byStream() {
    final HelloWorldController helloController = new HelloWorldController();
    final ResponseEntity<InputStreamResource> response = helloController.byStream();
    try (final InputStream input = response.getBody().getInputStream()) {
      final byte[] body = input.readAllBytes();
      Assertions.assertAll(
          () -> Assertions.assertEquals(response.getStatusCode(), HttpStatus.OK),
          () -> Assertions.assertEquals(Arrays.toString(body),
              Arrays.toString(HelloWorldController.HELLO_WORLD.getBytes()))
      );
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

GradleでSpring Bootを起動

コマンド実行

gradle bootRun

もしくは、

gradlew bootRun

APIの実行結果を確認

いずれも、Hello, world.がレスポンスボディで返却される(ブラウザに文言で出る)。

おまけ

InputStreamを返すタイプのレスポンスで、きちんとInputStreamがcloseされているか調べた。

  @RequestMapping(value = "/stream", method = RequestMethod.GET)
  ResponseEntity<InputStreamResource> byStream() {
    final InputStreamResource inputStreamResource = new InputStreamResource(
        new BufferedInputStreamCustom(new ByteArrayInputStream(HELLO_WORLD.getBytes()), BUFFER_SIZE)
    );

    final HttpHeaders headers = new HttpHeaders();
    headers.setContentLength(HELLO_WORLD.getBytes().length);

    return new ResponseEntity<>(inputStreamResource, headers, HttpStatus.OK);
  }

  private static class BufferedInputStreamCustom extends BufferedInputStream {

    public BufferedInputStreamCustom(InputStream in, int size) {
      super(in, size);
    }

    @Override
    public void close() throws IOException {
      System.out.println("closed");
      super.close();
    }

  }

APIを実行するたびに、コンソールにclosedが出力され、渡したInputStreamがきちんと閉じられていることがわかった。

mediado
私たちメディアドゥは、電子書籍を読者に届けるために「テクノロジー」で「出版社」と「電子書店」を繋ぎ、その先にいる作家と読者を繋げる「電子書籍取次」事業を展開しております。業界最多のコンテンツラインナップとともに最新のテクノロジーを駆使した各種ソリューションを出版社や電子書店に提供し、グローバル且つマルチコンテンツ配信プラットフォームを目指しています。
https://mediado.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away