kondoumh のブログ

- とあるソフトウェアエンジニアのめったに更新されないブログ -

Spring Initializr でコンテナフレンドリーな Spring Boot アプリ開発をスタートする

Spring はアプリケーション基盤として成熟しエンプラではデファクトスタンダードの地位を築いています。Spring Boot は Spring によるアプリケーション開発立ち上げを Boost してくれるフレームワークです。アプリケーションサーバ不要で Fat Jar をそのままデプロイできてクラウドやマイクロサービスとの親和性が高いため、数多くのオプションがモジュールとして公開されています。

Spring Initializr でプロジェクト名などの項目をポチポチ入力して送信するとソースコードを含むプロジェクトが生成され zip で降ってきます。

f:id:kondoumh:20181015094426p:plain

ビルドシステムは Gradle と Maven から選べます。Maven を選択すると Maven Wrapper (mvnw) が組み込まれ mvn コマンドのインストールも自動化されますので、事前準備は JDK インストールと環境変数 JAVA_HOME の設定だけ。あとは、IntelliJ IDEA や Eclipse にプロジェクトをインポートすればすぐコードを書き始められます。

Spring Boot はアプリケーションアーキテクチャにはノータッチです。トランザクションスクリプトでも DDD でも Clean Architecture でもお好みで。

大きめの開発プロジェクトでひな形を共有したい場合は、Spring Initializr で生成するのではなく、共通で利用するモジュールへの依存・パッケージ構造まで含めた雛形 (Maven Archetype や Gradle のプロジェクトテンプレート) を用意した方がよいでしょう。

Docker 対応

Dockerfile (DB を別コンテナにする場合などは docker-compose.yml も) を定義して Docker コンテナにアプリをデプロイしてテストし、プロダクション環境まで持っていけます。最近は Docker for Windows / Mac のおかげで Linux 仮想マシンを別途用意しなくてもよくなり、開発環境構築は非常に楽になりました*1

Spring Boot の設定ファイルは昔ながらの XML や properties に加え YAML に対応しています。最近は Docker や Swagger など YAML で定義ファイルを書くことが多いので、YAML で統一できるのはよいことです。環境依存の定義は @Configuration アノテーションを付与したクラスから値を読み込むのがお作法のようです。ポータブルなコンテナを構成するためには設定ファイルに値を書くよりは環境変数に寄せた方がよさそうです。

コンテナフレンドリーにするにはアプリの死活やリソース使用状況などをモニタリングできるようにすることも重要になってきます。

Spring Boot では spring-boot-starter-actuator というモジュールが提供されており、依存関係に組み込むだけで利用できます。

pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
         :

公開する health 機能を指定できます。この例では、health , env, metrics を指定しています。

application.yml

# Actuator Settings
management:
  health:
    defaults:
      enabled: false
  endpoints:
    web:
      base-path: /
      exposure:
        include: health, env, metrics

この他にも、リソース監視のためのツール Prometheus なども簡単に組み込めます。

Swagger の組み込み

クラウドに限らず REST な API は Open API (Swagger) でテスト可能な仕様を書いて開発することが普通になっています。Spring Boot アプリに Swagger を組み込むには、依存関係を追加して Configuration クラスを定義します。

pom.xml

    <dependencies>
        <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
        </dependency>
        <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
        </dependency>
         :

Configuration クラス*2

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(selector())
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Sample API")
                .description("SpringBoot Sample API")
                .version("1.0")
                .build();
    }

    // api のパスフィルタ
    private Predicate<String> selector() {
        return or(
                regex("/api/.*")
        );
    }
}

Controller クラスで、RequestMapping と RequestMethod を指定します*3

@RestController
@RequestMapping("/api")
public class CustomerController {

    @ResponseBody
    @RequestMapping(value = "/usr/{id}", method = RequestMethod.GET)
    public Customer getCustomer(@PathVariable("id") Long id) {
        :
        return customer; 
    }

    @ResponseBody
    @RequestMapping(value = "/user/", method = RequestMethod.POST)
    public Long registerCustomer(@RequestBody @Validated Customer customer) {
        :
        return id;
    }

}

実際の開発では、実装に先立って Swagger Editor で API 仕様を作成してプロジェクトに YAML または JSON 形式で含めて構成管理するとよいでしょう。API の開発が完了してなくても利用者側に動く仕様を提供できます。

おわりに

コンテナ技術の普及でサーバーサイド Java 開発も様変わりしていますね。Java のリリースサイクルの激変と Oracle の Java サポート有償化で Java 離れが進むとの予想もありますが、実際には「まだ終わらんよ」という感じだと思います。データクラスのサポートが弱い問題とか*4 Kotlin など AltJava 勢の出現に目を奪われがちではあるものの、これまでのソフトウェア資産、デベロッパーの調達などを考えるとすぐに廃れるとは思えません。

MongoDB でデータを CRUD する REST API サンプルプロジェクトを作ってみました。

github.com

*1:メモリも 8GB あればなんとか動くレベルです

*2:この例では設定値の読み込みはしていません。

*3:RequestMethod を指定しないと、Controller メソッドごとに 全ての RequestMethod のAPI ドキュメントが生成されてしまいます。

*4:Lombok などのライブラリ機能で補完可能です。