VS Code + Docker で Java 開発 - Spring Boot (Maven/Gradle)

に公開

はじめに

この記事は「VS Code + Docker + Gemini CLI で Java 開発(Maven 版)」の続きです。前回は、伝統的な Maven プロジェクトの構築と開発サイクルについて解説しました。

今回はその発展形として、モダンな Java アプリ開発のデファクトスタンダードである Spring Boot フレームワークを導入します。本記事では、ビルドツールが異なる 2 つのサンプルプロジェクトを通して、Spring Boot の強力な機能と、Maven と Gradle それぞれでのプロジェクト管理方法を探ります。

  • java-app004: Maven をベースにした Spring Boot プロジェクト
  • java-app005: Gradle をベースにした Spring Boot プロジェクト

これらのプロジェクトは、どちらも単純なメッセージを出力するアプリを実装するものなので、提供する機能は少ないです。ただし、Spring Boot というフレームワークを使っているため、このフレームワークを知らない人がコードを見ると、マジックワードがたくさん出てきて複雑に感じるはずです。

しかし、自分で実装することを考えたら手間のかかる機能があらかじめ用意されているので、簡単なアプリを作成するなら、このフレームワークを使えるようになっておいた方が良いです。特に、Java で新規の Web アプリを実装する場合は、Spring Boot の採用を検討する価値があります。

ということで、Gradle、Maven に続いて、VS Code で Spring Boot アプリを開発する方法について説明することにしました。

サンプルコード

GitHub にサンプルコードを用意したので、先に紹介しておきます。

dvc-java-gemini-01 のタグをつけてあります。

タグ 説明
dvc-java-gemini-01 今回、解説するもの

開発コンテナ用については、次の記事の「Gemini CLI 向け Dev Container のセットアップ」 の内容を理解している前提での説明となっています。ご了承ください。

環境の用意

本記事では、開発環境の差異をなくし、すぐに Java 開発を始められるように、Visual Studio Code の Dev Containers 拡張機能と、筆者が提供する開発コンテナーイメージ hiro345g/dvc:jdk-202507 を使用します。

hiro345g/dvc:jdk-202507 をベースとすることで、必要なツール(JDK, Maven, Gradle, Spring Boot CLI など)の用意が簡単に済む開発コンテナを用意できます。

ここでは、次のようなフォルダ構成で環境を用意するとします。

dvc-java-gemini/
├── .devcontainer/
│   ├── Dockerfile
│   ├── compose.yaml
│   └── devcontainer.json
├── java-app004/
└── java-app005/

Dev Container の設定は、.devcontainer フォルダに用意します。

devcontainer.json

このファイルは、Dev Container の主要な設定ファイルです。これまでのものと違う点は、Spring Boot Extension 用の拡張機能を追加してあることです。

devcontainer.json の詳細
.devcontainer/devcontainer.json
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/java
{
  "name": "dvc-java-gemini",
  "dockerComposeFile": "./compose.yaml",
  "service": "dvc-java-gemini",
  "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
  // `google.gemini-cli-vscode-ide-companion` 対応の暫定対応。
  // `host.docker.internal` を別用途で使うことを優先する場合は、
  // `"postCreateCommand":` の行はコメントアウトすること。
  "postCreateCommand": "echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts",
  "customizations": {
    "vscode": {
      "settings": {
        "terminal.integrated.defaultProfile.linux": "bash"
      },
      "extensions": [
        "vscjava.vscode-java-pack", // Java Extension Pack
        "vmware.vscode-boot-dev-pack", // Spring Boot Extension Pack
        "shengchen.vscode-checkstyle", // Checkstyle
        "ms-azuretools.vscode-containers", // Container Tools
        "docker.docker", // Docker DX
        "eamodio.gitlens", // Git Lens
        "donjayamanne.githistory", // Git History
        "mhutchie.git-graph", // Git Graph
        "google.gemini-code-assist", // Gemini Code Assist
        "google.gemini-cli-vscode-ide-companion" // Gemini CLI
      ]
    }
  }
}

compose.yaml

このファイルは、Docker Compose を使って開発環境コンテナのビルドと実行を定義します。

Maven や Gradle を使う場合、必要なパッケージをローカルにダウンロードしてキャッシュします。これについては、開発コンテナ用 Docker イメージに含める方法と、Docker ボリュームへ保存する方法があります。基本はキャッシュなので、筆者は Docker イメージには含めず、Docker ボリュームへ保存するようにしています。

そのため、ここでは、Maven (.m2) と Gradle (.gradle) のキャッシュ用フォルダを Docker ボリュームとしてマウントします。これにより、開発コンテナを一度破棄してから、起動しなおすことがあっても、ダウンロードした依存関係が保持され、ビルドが高速になります。

そのかわり、Docker ボリュームを別途管理する必要があります。管理しやすくするために、name: dvc-java-gemini-m2 のように自分でボリューム名を指定するようにしています。

compose.yaml の詳細
.devcontainer/compose.yaml
name: dvc-java-gemini
services:
  dvc-java-gemini:
    build: .
    image: dvc-java-gemini:0.0.2
    container_name: dvc-java-gemini
    hostname: dvc-java-gemini
    tty: true
    volumes:
      # dvc-java-gemini の Docker Compose プロジェクト用フォルダを
      # /workspaces/dvc-java-gemini にバインドマウント
      - type: bind
        source: ..
        target: /workspaces/dvc-java-gemini
      # Gemini 設定用フォルダをバインドマウント
      - type: bind
        source: ../.gemini
        target: /home/node/.gemini
      # Maven 用フォルダを Docker ボリュームへマウント
      - type: volume
        source: dvc-java-gemini-m2
        target: /home/node/.m2
      # Gradle 用フォルダを Docker ボリュームへマウント
      - type: volume
        source: dvc-java-gemini-gradle
        target: /home/node/.gradle

volumes:
  dvc-java-gemini-m2:
    name: dvc-java-gemini-m2
  dvc-java-gemini-gradle:
    name: dvc-java-gemini-gradle

Dockerfile

コンテナイメージの具体的な構築手順を定義します。ここでは、SDKMAN! を使って springboot 3.5.6 をインストールしています。また、Gradle や Maven がローカルにダウンロードしたファイルを保存するユーザーホーム配下のフォルダを用意して、マウントできるようにしてあります。

Dockerfile の詳細
.devcontainer/Dockerfile
FROM hiro345g/dvc:jdk-202507

# パッケージインストールのため root
USER root

# Linux で Gemini CLI を使う場合で、認証時に xdg-utils が必要と
# なることがあるため、インストールしておく。
RUN apt-get update \
    && apt-get install -y xdg-utils \
    && apt-get -y autoremove \
    && apt-get -y clean \
    && rm -rf /var/cache/apt /var/lib/apt/lists

# ここから node ユーザーによる作業
USER node

# SDKMAN! のインストール先を指定
ENV  SDKMAN_DIR=/usr/local/sdkman

# gemini-cli を node ユーザーでグローバルインストール
RUN npm install -g @google/gemini-cli

# sdk コマンドを使えるように設定
RUN bash -c 'echo "[[ -s \"/usr/local/sdkman/bin/sdkman-init.sh\" ]] && source \"/usr/local/sdkman/bin/sdkman-init.sh\"" >> /home/node/.bashrc'

# SDKMAN! による spring コマンドのインストール
RUN bash -c 'source /usr/local/sdkman/bin/sdkman-init.sh && sdk install springboot 3.5.6'

# Gradle, Maven 用ユーザーフォルダの用意
RUN bash -c 'mkdir /home/node/.m2 && mkdir /home/node/.gradle'

これらの設定により、VS Code でこのリポジトリを開き、「Reopen in Container」を選択するだけで、誰でも同じ開発環境を即座に再現できます。

前回までの開発コンテナの削除

今回の開発コンテナを使う前に、前回までの開発コンテナを起動している場合は、それを削除(docker compose down)しておきます。Docker in WSL Ubuntu の場合は WSL Ubuntu のターミナル、Docker Desktop の場合は Git Bash で次のコマンドを実行します。他の方法でもコンテナを削除できれば良いです。

docker compose -p dvc-java-gemini down

イメージのビルド

あらかじめ dvc-java-gemini:0.0.2 のイメージを docker compose build でビルドしておくようにします。手元の Docker in WSL Ubuntu では、dvc-java-gemini:0.0.1 イメージがある環境だと、dvc-java-gemini:0.0.2 のイメージを見つけることができなくなって、エラーとなってしまいました。

Docker Desktop を使っている場合は、Git Bash でコマンド実行するとし、~/workspace/dvc-java-gemini/.devcontainer/compose.yaml ファイルがあるとして、次のコマンドを実行します。

PROJECT_DIR=~/workspace/dvc-java-gemini
COMPOSE_FILE="${PROJECT_DIR}/.devcontainer/compose.yaml"
docker compose -f "${COMPOSE_FILE}" build

Docker in WSL Ubuntu を使っている場合は、ターミナルで WSL Ubuntu を開きます。compose.yaml の Windows 側のパスが Docker Desktop を使っている場合と同じ Git Bash 上で ~/workspace/dvc-java-gemini/.devcontainer/compose.yaml の場合は、~/mnt/c/Users/user001 と置き換えたパスを指定すれば良いです。

PROJECT_DIR=/mnt/c/Users/user001/workspace/dvc-java-gemini
COMPOSE_FILE="${PROJECT_DIR}/.devcontainer/compose.yaml"
docker compose -f "${COMPOSE_FILE}" build

実行例は次のようになります。

$ PROJECT_DIR=/mnt/c/Users/user001/workspace/dvc-java-gemini
$ COMPOSE_FILE="${PROJECT_DIR}/.devcontainer/compose.yaml"
$ docker compose -f "${COMPOSE_FILE}" build
[+] Building 65.2s (12/12) FINISHED
(略)
 => [internal] load metadata for docker.io/hiro345g/dvc:jdk-202507 (略)
(略)
 => exporting to image (略)
(略)
 ✔ dvc-java-gemini:0.0.2  Built

開発コンテナの開始と拡張機能の無効化

準備ができたら、VS Code を起動します。

cd ${PROJECT_DIR}
code .

通知に「コンテナーで再度開く」が表示されたら、それをクリックします。

開発コンテナーが開くと、自動で Java Extension Pack 拡張機能などがインストールされて有効化されます。ただし、用意してある環境では java-app004java-app005 の両方に対して Java プロジェクトの自動認識がされてしまい、事前準備の重い処理が動いてしまいます。

処理が終わるのを待っていても良いのですが、一番良いのは、このワークスペース(/home/node/workspace/dvc-java-gemini)では Java Extension Pack 拡張機能を無効化しておくことです。拡張機能のビューを表示して、開発コンテナー:dvc-java-gemini:インストール済み の一覧に含まれる Java Extension Pack 拡張機能を無効化します。これに含まれる拡張機能を無効化するかの確認ダイアログが表示されたら「はい」をクリックして全部無効化します。

/images/20250927_vscode_java_05/01.png
Java Extension Pack 拡張機能を無効化

この後、java-app004.code-workspacejava-app005.code-workspace を開いたときに、Java Extension Pack 拡張機能を有効化するようにしてください。

Spring Boot とは

Spring Boot は、Java アプリ開発で広く使われている Spring Framework を、より迅速かつ容易に利用できるように設計されたフレームワークです。公式のサイトは次の URL で公開されています。

このフレームワークの目的は、開発者が複雑な設定に時間を費やすことなく、アプリのビジネスロジックの実装に集中できるようにすることです。

主な特徴は次のとおりです。

  • 自動設定 (Auto-Configuration)
  • スタンドアロン実行
  • スターター依存関係 (Starter Dependencies)

自動設定 (Auto-Configuration) とは、プロジェクトに含まれるライブラリを検知し、それに応じて必要となるであろう設定を自動的に行うことです。例えば、データベースを使うアプリについて、Spring Boot を使わない場合の Java アプリでは、定型的なデータベース接続用のコードを開発者が記述する必要がありました。Spring Boot では、この自動設定が対応する範囲が非常に広いため、開発者が記述しないといけないデータベース接続用のコードが非常に少なくなっています。

スタンドアロン実行とは、Web アプリでも単体の JAR ファイルで実行できることです。通常の Java で実装した Web アプリだと、別途 Apache Tomcat サーバーなどの Jakarta EE コンテナが必要となります。しかし、Spring Boot を使うと、Tomcat のような Jakarta EE コンテナをアプリに内蔵することが簡単にできます。そのため、別途サーバーを用意しなくても、JAR ファイルを実行するだけでアプリを起動できます。

スターター依存関係 (Starter Dependencies) とは、「Web アプリ開発」や「データベースアクセス」といった特定の機能ごとに、必要となるライブラリ一式をまとめた「スターター」が提供されていることです。これにより、依存関係の管理の手間が大幅に簡素化できます。

これらの特徴により、Spring Boot は本番環境に対応した堅牢なアプリや、マイクロサービスを迅速に構築するためのデファクトスタンダードとなっています。

Spring Boot プロジェクトの概要

ここで用意してある java-app004java-app005 は、ビルドツールは異なりますが、アプリの機能としては全く同じものです。どちらも Spring Boot を基盤としたスタンドアロンのコンソールアプリであり、CommandLineRunner という仕組みを利用して、起動後に特定の処理を実行して終了します。

最初から Web アプリを動かそうとすると、必要となる前提知識の量が跳ね上がります。まずは、ここで紹介する Spring Boot アプリの基本構造について理解してから、次の Spring Boot アプリ開発へステップを踏みながら進むのが良いと考えています。ということで、本記事では、あえて単純な機能しか持たないアプリとしてあります。

なお、ここでは java-app004/src にあるものについて説明しています。java-app005/src にあるものは app004app005 に変更するなど、パッケージ名やクラス名に若干の変更をしてありますが、内容的には同じプログラムとなっています。そのため、説明は省略しています。

ここで、Java のソースコードを参照するときは、VS Code で java-app004.code-workspace のワークスペースを開いてから、Java Extension Pack 拡張機能を有効化します。それから、Java のコードをエディタで開くと、Java Extension Pack 拡張機能によるフォーマットや文法チェックが利用できる実際の開発画面での確認ができます。

/images/20250927_vscode_java_05/02.png
java-app004.code-workspace を開いた VS Code 画面

最近使ったワークスペースを開く

java-app004.code-workspace を開いている状態から、初めて java-app005.code-workspace を開くには、一度 /home/node/workspace/dvc-java-gemini のワークスペースに戻ってから、そこで VS Code の「エクスプローラー」から java-app005.code-workspace をエディタで開き、「ワークスペースを開く」ボタンをクリックします。

こういった手順を踏む場合は、VS Code のメニューにある「ファイル」-「最近使用した項目を開く」を使うと、ワークスペースの切り替えが楽にできます。

/images/20250927_vscode_java_05/03.png
最近使用した項目を開く

ターミナルに慣れている場合は、java-app005.code-workspace をターミナルから code コマンドで開いても良いです。その場合は、java-app004.code-workspace を開いている VS Code の画面で開発コンテナのターミナルを開き、次のコマンドを実行します。ただし、こちらは新しく開発コンテナーをアタッチした VS Code の画面が表示されます。

code /workspace/dvc-java-gemini/java-app005/java-app005.code-workspace

アプリの構成

java-app004、java-app005 の Spring Boot アプリの起動の仕組みは、主に 3 つのクラスによって成り立っています。

  • JavaApp004Application
  • App
  • GreetingProperties

JavaApp004Application:

このクラスは、@SpringBootApplication アノテーションが付与された、アプリ起動の起点(エントリーポイント)となるものです。

JavaApp004Application.java
package internal.dev.javaapp004;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

/**
 * Main application class for java-app004.
 */
@SpringBootApplication(scanBasePackages = "internal.dev")
@EnableConfigurationProperties(GreetingProperties.class)
public class JavaApp004Application {

  /**
   * Default constructor.
   */
  public JavaApp004Application() {
    // Default constructor
  }

  /**
   * Main entry point for the application.
   *
   * @param args command line arguments.
   */
  public static void main(String[] args) {
    SpringApplication.run(JavaApp004Application.class, args);
  }
}
  • @SpringBootApplication: 設定クラスの宣言、自動設定の有効化、コンポーネントのスキャンという 3 つの重要な機能をまとめて提供します。scanBasePackages を指定することで、DI コンテナが管理するコンポーネントを探す範囲を internal.dev パッケージ以下に限定しています。
  • @EnableConfigurationProperties(GreetingProperties.class): GreetingProperties クラスをプロパティファイル(application.properties)から値を読み込むための設定クラスとして有効にします。
  • SpringApplication.run(...): 組み込み Web サーバーの起動や DI コンテナの初期化などを含む、Spring Boot アプリの起動処理を実行します。

GreetingProperties:

このクラスは @ConfigurationProperties(prefix = "greeting") アノテーションを持ち、application.properties ファイル内の greeting. から始まるプロパティをマッピングするためのクラスです。

このクラスを用意することで、アプリの挙動を開発時と運用時とで変えることが簡単にできるようになります。

GreetingProperties.java
package internal.dev.javaapp004;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * Configuration properties for greeting messages.
 */
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {

  private String message;

  /**
   * Default constructor.
   */
  public GreetingProperties() {
    // Default constructor
  }

  /**
   * Gets the greeting message.
   *
   * @return The greeting message.
   */
  public String getMessage() {
    return message;
  }

  /**
   * Sets the greeting message.
   *
   * @param message The greeting message to set.
   */
  public void setMessage(String message) {
    this.message = message;
  }
}

ここでは、greeting.message というキーの値が、このクラスの message フィールドに自動的に設定されるようにしてあります。これにより、設定値を型安全に扱うことができます。

App:

このクラスは、CommandLineRunner インターフェースを実装し、アプリのメインロジックを記述します。

App.java
package internal.dev;

import internal.dev.javaapp004.GreetingProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

/**
 * The main application class.
 */
@Component
public class App implements CommandLineRunner {

  /** The logger. */
  private static final Logger logger = LoggerFactory.getLogger(App.class);

  private final GreetingProperties greetingProperties;

  /**
   * Constructs the App with GreetingProperties.
   *
   * @param greetingProperties The properties for greeting.
   */
  public App(GreetingProperties greetingProperties) {
    this.greetingProperties = greetingProperties;
  }

  @Override
  public void run(String... args) throws Exception {
    logger.info("This is an info message.");
    logger.warn("This is a warning message.");
    logger.error("This is an error message.");
    logger.info("Message from .env: {}", greetingProperties.getMessage());
  }

  /**
   * Returns a greeting message.
   *
   * @return The greeting from .env file
   */
  public String getGreeting() {
    return greetingProperties.getMessage();
  }
}
  • @Component: このクラスが Spring の DI コンテナによって管理されるべきコンポーネント(部品)であることを示します。
  • CommandLineRunner: このインターフェースを実装すると、run メソッドが Spring Boot アプリの起動処理が完了したに自動的に呼び出されます。ここに、コンソールアプリのエントリーとなる処理を記述するのが最適です。
  • コンストラクタインジェクション: App クラスはコンストラクタで GreetingProperties を受け取ります。Spring の DI コンテナが、@EnableConfigurationProperties で有効化された GreetingProperties のインスタンスを自動的に生成し、コンストラクタを通じて注入(DI)します。

プロパティファイル

Spring Boot では、src/main/resources ディレクトリに置かれた application.properties ファイルを使って、アプリの挙動を柔軟に設定できます。

このプロジェクトでは、さらにプロファイルという機能を利用して、アプリ実行時の設定を簡単に切り替えられるようにしています。ここでは、2 つのプロファイルを用意してあります。

プロファイル 説明
dev 開発環境用
prod 運用環境用

プロファイル機能と設定の優先順位

Spring Boot は、有効になっているプロファイルに応じて、読み込むプロパティファイルを変更することができます。

  1. application.properties
  2. application-{profile}.properties

application.properties:

これは、すべてのプロファイルで共通して読み込まれる基本設定ファイルです。

このファイルで使用するプロファイルを指定することができます。プロファイルを指定するために使うプロパティは、 spring.profiles.active です。

application-{profile}.properties:

これは、spring.profiles.active で指定されたプロファイルに応じた設定ファイルとなります。例えば、プロファイル dev を用意する場合は、application-dev.properties ファイルを作成します。ここで定義された値は、application.properties の共通設定を上書きします。

このプロジェクトでは、application.propertiesspring.profiles.active=dev と記述しているため、デフォルトでは dev プロファイルが有効になります。

src/main/resources/application.properties
spring.profiles.active=dev

プロファイルごとの設定ファイル

application-dev.properties は、dev プロファイル(開発環境用)が有効な場合に読み込まれます。

src/main/resources/application-dev.properties
greeting.message=This is a message for the dev environment.

application-prod.properties は、prod プロファイル(運用環境用)が有効な場合に読み込まれます。Maven でビルドまたは実行する際に -P prod オプションを付けると、こちらの設定が有効になります。

src/main/resources/application-prod.properties
greeting.message=This is a message for the prod environment.

ロギング設定との連携

このプロファイル機能は、ロギング設定にも活用できます。logback.xml の中で <springProfile> タグを使うと、有効なプロファイルに応じてログのレベルや出力先を切り替えることができます。

ここでは、開発環境ではコンソール出力とログファイルへの出力とし、運用環境ではログファイルのみに出力とすることを基本とし、ログレベルも調整して設定します。

src/main/resources/logback.xml
<configuration>
  <!-- ... Appender definitions ... -->

  <!-- dev profile -->
  <springProfile name="dev">
    <root level="INFO">
      <appender-ref ref="STDOUT" />
      <appender-ref ref="FILE" />
    </root>
  </springProfile>

  <!-- prod profile -->
  <springProfile name="prod">
    <root level="WARN">
      <appender-ref ref="FILE" />
    </root>
  </springProfile>

</configuration>

dev プロファイルでは、ログレベルは INFO で、コンソール (STDOUT) とファイル (FILE) の両方に出力される指定となっています。

prod プロファイルでは、ログレベルは WARN に引き上げられ、出力先はファイルのみに限定される指定となっています。これにより、運用環境で不必要なログが出力されるのを防いてでいます。

JUnit 5 でのテスト

Spring Boot は spring-boot-starter-test を通じて、JUnit 5 をベースとした強力なテスト環境を提供します。

ここでは、サンプルとして JavaApp004ApplicationTests.java を用意してあります。

JavaApp004ApplicationTests.java
package internal.dev.javaapp004;

import static org.junit.jupiter.api.Assertions.assertEquals;

import internal.dev.App;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class JavaApp004ApplicationTests {

  @Autowired
  private App app;

  @Test
  void testGetGreeting() {
    assertEquals("Hello from test-application.properties!", app.getGreeting());
  }

}

@SpringBootTest は、テスト実行時に実際のアプリ起動に近い形で DI コンテナをセットアップします。このとき、src/test/resources 内の設定ファイルが優先的に読み込まれます。

@Autowired は、DI コンテナに登録されている App クラスのインスタンスを、テストクラスのフィールドに自動的に注入します。これにより、実際のコンポーネントを使った統合テストが容易になります。

testGetGreeting メソッドでは、src/test/resources/application.properties に定義された greeting.message の値が正しく読み込まれていることを検証しています。

src/test/resources/application.properties の内容は次のようにしてあります。

src/test/resources/application.properties
greeting.message=Hello from test-application.properties!

Maven によるビルド: java-app004

java-app004 は、Java プロジェクトで長年広く使われているビルドツールである Maven を採用しています。

プロジェクト構造(Maven 版)

標準的な Maven のディレクトリ構成に従っています。ビルド設定は pom.xml に記述されます。

java-app004/
├── src/
│   └── main/
│       ├── java/...
│       └── resources/...
├── .gitignore
├── mvnw              # Maven Wrapper
├── pom.xml           # Maven ビルド設定
└── README.md

ビルド設定 (pom.xml)

Spring Boot プロジェクトの pom.xml から、Spring Boot 関連の主な設定を抜粋したものが以下になります。

pom.xml(抜粋)
<!-- Spring Boot のバージョン管理を集約 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.6</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <!-- 基本的な Spring Boot 機能のスターター -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- 開発時の生産性を向上させるためのツール -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>

    <!-- @ConfigurationProperties を使うために必要 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- テスト用のスターター (JUnit 5, Mockito などを含む) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- 実行可能な JAR を作成するためのプラグイン -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

主要な依存関係とプラグインは以下の通りです。

  • <parent>: spring-boot-starter-parent を指定することで、多数のライブラリのバージョンが一元管理され、互換性の問題を気にする必要がなくなります。
  • spring-boot-starter: DI コンテナ、ロギング、自動設定など、Spring Boot の中核機能を提供します。
  • spring-boot-devtools: 開発時の生産性を向上させるためのツールです。ファイルの変更を検知してアプリを自動的に再起動する機能などを提供します。
  • spring-boot-configuration-processor: @ConfigurationProperties を使用したクラス(GreetingProperties)のメタデータを生成し、IDE での入力補完などを可能にするための依存関係です。
  • spring-boot-maven-plugin: 依存ライブラリをすべて含んだ「実行可能な JAR (fat JAR)」を生成します。

ここに次の依存関係とプラグインを追加して、pom.xml を完成させました。

  • lombok: @Getter@Setter、コンストラクタなどをアノテーションで自動生成し、Java の定型的なコードを削減します。
  • maven-checkstyle-plugin: Google Java Style Guide への準拠をビルド時に自動的にチェックします。
  • maven-javadoc-plugin: Javadoc コメントから API ドキュメントを生成します。
  • <profiles>: Maven のプロファイルを定義します。devproddebug の各プロファイルがあり、ビルドや実行の挙動を切り替えるために使用されます。例えば prod プロファイルでは、Spring Boot の prod プロファイルを有効にして JAR をパッケージングします。

Maven プロファイル

pom.xml<profiles> セクションでは、Maven のプロファイルを定義し、ビルドや実行の挙動を切り替えることができます。

ここで、Spring Boot アプリ用のプロファイルは対象範囲が Spring Boot アプリですが、こちらは運用環境と開発環境全体を対象としたものとなる点に注意してください。Maven プロジェクトでは、Spring Boot アプリが依存するパッケージや、開発で使用する周辺ツールのバージョン管理は Maven ですることになります。

この場合、例えばフォーマッターやスタイルチェッカーは何を使うのか、開発時と運用時とで周辺ツールの設定をどのように変更するのか、といったことは Spring Boot アプリとは別に管理する必要があります。そういった管理ができるように、Maven のプロファイルがあります。

java-app004 の Maven プロジェクトでは、次の 3 つのプロファイルを用意してあります。

プロファイル 説明
dev 開発環境用
prod 運用環境用
debug デバッグ実行用
pom.xml (プロファイル部分の抜粋)
<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <spring.profiles.active>dev</spring.profiles.active>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <spring.profiles.active>prod</spring.profiles.active>
        </properties>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <version>3.1.0</version>
                    <executions>
                        <execution>
                            <phase>process-resources</phase>
                            <goals>
                                <goal>run</goal>
                            </goals>
                            <configuration>
                                <target>
                                    <replace
                                        file="${project.build.outputDirectory}/application.properties"
                                        token="spring.profiles.active=dev"
                                        value="spring.profiles.active=prod" />
                                </target>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
    <profile>
        <id>debug</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <jvmArguments>
                            -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005
                        </jvmArguments>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

dev プロファイルについては、<activation><activeByDefault>true</activeByDefault></activation> により、プロファイルが明示的に指定されない場合のデフォルトとして設定されています。

prod プロファイルについては、-P prod で有効になります。プロパティ spring.profiles.activeprod にすることで、Spring Boot アプリを実行するときに、prod プロファイルを有効にします。

なお、ここでは ビルド時に -P prod が指定されたときは、application.properties ファイル内の spring.profiles.active の値を prod にするようにしてあります。アプリ実行時に spring.profiles.active を指定する方法でプロファイルを切り替える場合は必要ありませんが、Maven のプロファイル利用の例として置換処理をしています。置換処理には、maven-antrun-plugin プラグインを使っています。replace 要素の指定で spring.profiles.active=devspring.profiles.active=prod に置き換えています。

debug プロファイルについては、-P debug で有効になります。spring-boot-maven-plugin の設定で、リモートデバッグを有効にするための jvmArguments を追加します。

完成版の `pom.xml`
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.6</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>internal.dev</groupId>
    <artifactId>java-app004</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>java-app004</name>
    <description>Demo project for Spring Boot</description>
    <url />
    <licenses>
        <license />
    </licenses>
    <developers>
        <developer />
    </developers>
    <scm>
        <connection />
        <developerConnection />
        <tag />
        <url />
    </scm>
    <properties>
        <java.version>21</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.eclipse.m2e</groupId>
                    <artifactId>lifecycle-mapping</artifactId>
                    <version>1.0.0</version>
                    <configuration>
                        <lifecycleMappingMetadata>
                            <pluginExecutions>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>org.apache.maven.plugins</groupId>
                                        <artifactId>maven-checkstyle-plugin</artifactId>
                                        <versionRange>[3.4.0,)</versionRange>
                                        <goals>
                                            <goal>check</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <ignore />
                                    </action>
                                </pluginExecution>
                            </pluginExecutions>
                        </lifecycleMappingMetadata>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <source>21</source>
                    <target>21</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-checkstyle-plugin</artifactId>
                <version>3.4.0</version>
                <dependencies>
                    <dependency>
                        <groupId>com.puppycrawl.tools</groupId>
                        <artifactId>checkstyle</artifactId>
                        <version>10.17.0</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <configLocation>google_checks.xml</configLocation>
                    <consoleOutput>true</consoleOutput>
                    <failsOnError>true</failsOnError>
                    <linkXRef>false</linkXRef>
                </configuration>
                <executions>
                    <execution>
                        <id>validate</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>3.8.0</version>
                <executions>
                    <execution>
                        <id>attach-javadocs</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <reporting>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.6.2</version>
                <reportSets>
                    <reportSet>
                        <reports>
                            <report>ci-management</report>
                            <report>dependencies</report>
                            <report>dependency-info</report>
                            <report>dependency-management</report>
                            <report>distribution-management</report>
                            <report>index</report>
                            <report>issue-management</report>
                            <report>licenses</report>
                            <report>mailing-lists</report>
                            <report>modules</report>
                            <!-- <report>plugin-management</report> -->
                            <report>plugins</report>
                            <report>scm</report>
                            <report>summary</report>
                            <report>team</report>
                        </reports>
                    </reportSet>
                </reportSets>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-report-plugin</artifactId>
                <version>3.3.1</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>3.8.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jxr-plugin</artifactId>
                <version>3.4.0</version>
            </plugin>
        </plugins>
    </reporting>

    <profiles>
        <profile>
            <id>dev</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <spring.profiles.active>dev</spring.profiles.active>
            </properties>
        </profile>
        <profile>
            <id>prod</id>
            <properties>
                <spring.profiles.active>prod</spring.profiles.active>
            </properties>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-antrun-plugin</artifactId>
                        <version>3.1.0</version>
                        <executions>
                            <execution>
                                <phase>process-resources</phase>
                                <goals>
                                    <goal>run</goal>
                                </goals>
                                <configuration>
                                    <target>
                                        <replace
                                            file="${project.build.outputDirectory}/application.properties"
                                            token="spring.profiles.active=dev"
                                            value="spring.profiles.active=prod" />
                                    </target>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <id>debug</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <jvmArguments>
                                -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005
                            </jvmArguments>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

ビルドと実行(mvnw

このプロジェクトでは、Maven のプロファイル機能を利用して、環境に応じたビルドや実行が可能です。

ビルド(mvnw

dev プロファイルでのビルド (デフォルト):

次のコマンドを実行すると、dev プロファイルが有効な状態でアプリがビルドされます。

./mvnw clean install

実行例は次のようになります。最後に BUILD SUCCESS のメッセージが表示されたら成功です。失敗すると、このメッセージは表示されずに処理が終了します。

node ➜ /workspaces/dvc-java-gemini/java-app004 $ ./mvnw clean install
(略)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  48.819 s
[INFO] Finished at: 2025-09-27T23:18:40Z
[INFO] ------------------------------------------------------------------------

なお、処理の途中に、パッケージダウンロードのメッセージが表示されるなどします。処理のログとして使えるので、確認したいことがある場合に役立ちます。

prod プロファイルでのビルド:

本番環境向けのビルドを行うには、-P prod オプションを指定します。これにより、Spring Boot の prod プロファイルが有効化された JAR ファイルが生成されます。

./mvnw clean install -P prod

dev プロファイルでのビルドと同様に、最後に BUILD SUCCESS のメッセージが表示されたら成功です。

ビルド結果の jar の実行:

ビルド結果の jar は、どちらのプロファイルも target/java-app004-0.0.1-SNAPSHOT.jar にあります。これは、ビルド時のログからわかります。

[INFO] 
[INFO] --- jar:3.4.2:jar (default-jar) @ java-app004 ---
[INFO] Building jar: /workspaces/dvc-java-gemini/java-app004/target/java-app004-0.0.1-SNAPSHOT.jar

これを実行するには java -jar コマンドを使います。

java -jar target/java-app004-0.0.1-SNAPSHOT.jar

ビルド時に指定したプロファイルによって、動作が変わります。dev プロファイルのものはコンソールにメッセージ出力をしますが、prod プロファイルのものは出力しません。実行例は、この後に説明する「 実行(mvnw)」の方で示すので、ここでは省略します。

なお、コンソール出力を抑制したい場合は、次のようにログレベルの変更指定(--logging.level.root=ERROR)と、Spring のバナー非表示の指定(--spring.main.banner-mode=off)をします。

java -jar target/java-app004-0.0.1-SNAPSHOT.jar \
  --logging.level.root=ERROR \
  --spring.main.banner-mode=off

このとき、ch.qos.logback 関連のログ出力は残ります。これを抑制したい場合は、Logback設定ファイル(logback-spring.xml)を使えば良いようですが、ここでは対応していません。

実行(mvnw

dev プロファイルでの実行 (デフォルト):

-P オプションを指定しない場合、デフォルトで dev プロファイルが有効になります。

./mvnw spring-boot:run

実行例は次のようになります。Message from .env: の値から、dev プロファイル用のプロパティファイルが読み込まれていることがわかります。

node ➜ /workspaces/dvc-java-gemini/java-app004 $ ./mvnw spring-boot:run
(略)
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.5.6)

(略)
2025-09-27 23:20:24.836 [restartedMain] INFO  i.d.javaapp004.JavaApp004Application - Started (略)
2025-09-27 23:20:24.851 [restartedMain] INFO  internal.dev.App - This is an info message.
2025-09-27 23:20:24.852 [restartedMain] WARN  internal.dev.App - This is a warning message.
2025-09-27 23:20:24.852 [restartedMain] ERROR internal.dev.App - This is an error message.
2025-09-27 23:20:24.853 [restartedMain] INFO  internal.dev.App - Message from .env: This is a message for the dev environment.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.903 s
[INFO] Finished at: 2025-09-27T23:20:25Z
[INFO] ------------------------------------------------------------------------

prod プロファイルでの実行:

本番環境用の設定で実行するには、-P prod オプションを指定します。

./mvnw spring-boot:run -P prod

次に実行例を示します。先に現在時刻を確認するために、date -u コマンドを実行しておきましょう。ログの時刻を記録するにあたって、タイムゾーンは UTC としてあるので、-u オプションを指定します。ちなみに、日本の東京のタイムゾーンを使う場合は、TZ='Asia/Tokyo' の環境変数を指定します。

node ➜ /workspaces/dvc-java-gemini/java-app004 $ date -u
2025927日 土曜日 23:23:00 UTC
node ➜ /workspaces/dvc-java-gemini/java-app004 $ TZ='Asia/Tokyo' date
2025928日 日曜日 08:23:06 JST

ここでは、時刻確認をしてから、mvnw コマンドでアプリを実行し、その後に tail コマンドでログを確認しています。tail コマンドはファイルの末尾のテキストを表示するときに使うコマンドです。

dev プロファイルと違って、コンソール出力にはアプリによるメッセージ出力はなく、ログファイルの log/app.log にメッセージ出力がされていることがわかります。

node ➜ /workspaces/dvc-java-gemini/java-app004 $ TZ=C date
2025927日 土曜日 23:37:53 
node ➜ /workspaces/dvc-java-gemini/java-app004 $ ./mvnw spring-boot:run -P prod
[INFO] Scanning for projects...
(略)
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.5.6)

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.250 s
[INFO] Finished at: 2025-09-27T23:38:15Z
[INFO] ------------------------------------------------------------------------
node ➜ /workspaces/dvc-java-gemini/java-app004 $ tail logs/app.log 
2025-09-27 23:38:14.746 [restartedMain] WARN  internal.dev.App - This is a warning message.
2025-09-27 23:38:14.750 [restartedMain] ERROR internal.dev.App - This is an error message.

debug プロファイルでの実行:

デバッグモードでアプリを起動し、リモートデバッガの接続をポート 5005 で待ち受けるには、-P debug オプションを指定します。

./mvnw spring-boot:run -P debug

デバッガを接続しないで、アプリを強制終了するには、起動したターミナルで Ctrl + C を入力します。

ドキュメントとレポートの生成(Maven 版)

Maven のプラグインを使って、テストレポートや API ドキュメントを生成できます。

テストレポートを含むサイトを生成するには次のコマンドを実行します。

./mvnw site

レポートは target/site/surefire-report.html に生成されます。

Javadoc だけ生成することもできます。その場合は次のコマンドを実行します。

./mvnw javadoc:javadoc

Java API ドキュメントは target/site/apidocs/ に生成されます。

生成されたものについて、JDK 18 以降に付属する jwebserver コマンドを使用して target/site をドキュメントルートとする Web サーバーを起動することで、Web ブラウザから確認できます。

cd target/site
jwebserver -p 8000

Dev Container 内で実行する場合は、-b 0.0.0.0 を指定してすべてのネットワークインターフェースにバインドすることで、ホストマシンのブラウザからアクセスできるようになります。

cd target/site
jwebserver -b 0.0.0.0 -p 8000

以上で、Maven 版の説明はおしまいです。

Gradle によるビルド: java-app005

java-app005 は、java-app004 と全く同じ機能を持つプロジェクトですが、ビルドツールとしてよりモダンで柔軟な Gradle を採用しています。

プロジェクト構造(Gradle 版)

pom.xml の代わりに build.gradlesettings.gradle がビルド設定を担います。

java-app005/
├── src/
│   └── main/
│       ├── java/...
│       └── resources/...
├── .gitignore
├── gradlew           # Gradle Wrapper
├── build.gradle      # Gradle ビルドスクリプト
└── README.md

ビルド設定 (build.gradle)

Gradle のビルドスクリプトは、XML ではなく Groovy や Kotlin といったプログラミング言語で記述するため、より柔軟で可読性が高いとされています。さらにこのプロジェクトでは、依存関係のバージョンを gradle/libs.versions.toml というバージョンカタログファイルで一元管理する、モダンなアプローチを採用しています。

バージョンカタログ (libs.versions.toml)

このファイルでは、プロジェクト全体で使用する依存関係を [versions][libraries][plugins] の 3 つのセクションで一元管理します。

gradle/libs.versions.toml
[versions]
springBoot = "3.5.6"
springDependencyManagement = "1.1.6"
checkstyleTool = "10.17.0"

[libraries]
lombok = { group = "org.projectlombok", name = "lombok" }
junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
spring-boot-starter = { group = "org.springframework.boot", name = "spring-boot-starter" }
spring-boot-devtools = { group = "org.springframework.boot", name = "spring-boot-devtools" }
spring-boot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test" }

[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "springBoot" }
spring-dependency-management = { id = "io.spring.dependency-management", version.ref = "springDependencyManagement" }

[versions] には、ライブラリやプラグインのバージョン番号を変数として定義します。これにより、複数の場所で使われるバージョンをまとめて更新できます。

[libraries] には、依存するライブラリを定義し、build.gradleから参照するためのエイリアス(例: spring-boot-starter)を設定します。

[plugins] には、ビルドに使用する Gradle プラグインを定義します。ここでもエイリアス(例: spring-boot)を設定し、バージョンは [versions] セクションで定義した変数を参照しています。

ビルドスクリプト (build.gradle)

build.gradle では、バージョンカタログで定義したエイリアスを使って、プラグインや依存関係を簡潔に記述します。

build.gradle
plugins {
    alias(libs.plugins.spring.boot)
    alias(libs.plugins.spring.dependency.management)
    id 'java'
    id 'checkstyle'
}

group = 'internal.dev'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation libs.spring.boot.starter
    developmentOnly libs.spring.boot.devtools
    compileOnly libs.lombok
    annotationProcessor libs.lombok
    testImplementation libs.spring.boot.starter.test
    testRuntimeOnly libs.junit.platform.launcher
}

// ... (checkstyle, profiles, etc.)

plugins には、プロジェクトで使用する Gradle プラグインを宣言します。aliasには、バージョンカタログ (libs.versions.toml) で定義されたプラグインのエイリアスを参照して適用します。例えば alias(libs.plugins.spring.boot) は、Spring Boot プラグインを適用します。id には、プラグインの ID を直接指定して適用します。java プラグインは Java のコンパイルやテストなどの基本的なタスクを、checkstyle プラグインはコードの静的解析機能を提供します。

groupversion には、それぞれ、アプリを開発しているグループ情報とバージョン情報を指定します。なお、Maven では、パッケージの識別に Maven コーディネート(Maven coordinates)を使います。このアプリも Maven コーディネートを持ち、その値の決定には、これらの指定が影響します。Maven コーディネートは、group:artifact:version というフォーマットで決まる識別子で、settings.gradlerootProject.nameartifact として使われ、build.gradle ファイルで指定する groupversion と合わせて決まります。

java { toolchain { ... } } には、プロジェクトで使用する Java のバージョン(ツールチェーン)を指定します。ここでは Java 21 が設定されています。

repositories { ... } には、依存関係をダウンロードしてくるリポジトリを指定します。mavenCentral() は、Maven Central リポジリを使用することを意味します。

dependencies { ... } には、プロジェクトの依存ライブラリを定義します。implementation, developmentOnly, compileOnly, annotationProcessor, testImplementation などは「コンフィグレーション」と呼ばれ、依存関係がどのクラスパスに含まれるか(コンパイル時、実行時、テスト時など)を決定します。libs.spring.boot.starter のように、バージョンカタログで定義したライブラリのエイリアスを参照して、タイプセーフに依存関係を追加できます。

Gradle でのプロファイル対応

build.gradle の末尾には、Maven のプロファイルと同様の機能を実現するための設定が記述されています。

build.gradle (抜粋)
// プロファイルの指定がない場合は 'dev' を使う
ext.profile = project.hasProperty('profile') ? project.getProperty('profile') : 'dev'

// application.properties 内のプレースホルダを置換するフィルター
tasks.withType(ProcessResources) {
    filesMatching('**/application.properties') {
        expand(profile: project.ext.profile)
    }
}

// bootRun タスクにプロファイルを渡す
tasks.withType(org.springframework.boot.gradle.tasks.run.BootRun) {
    args = ["--spring.profiles.active=${profile}"]
}

// debug プロパティがあればデバッグ用のJVM引数を追加
if (project.hasProperty('debug')) {
    tasks.withType(JavaExec) {
        jvmArgs = ['-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005']
    }
}

// bootJar タスクで生成されるJARのマニフェストにプロファイルを含める
tasks.withType(org.springframework.boot.gradle.tasks.bundling.BootJar) {
    manifest {
        attributes(
            'Spring-Boot-Profiles': profile
        )
    }
}

// 現在のプロファイルを表示するタスク
task showProfile {
    doLast {
        println "Current profile: ${profile}"
    }
}

ext.profile = ... は、Gradle のコマンドラインで -Pprofile=... というプロパティが指定されていればその値を、なければ 'dev'profile という変数に設定するというものです。

tasks.withType(ProcessResources) は、タスク実行時にリソースに対して処理をするもので、ここでは application.properties 内の ${profile} をプロパティで指定された値に置換するというものです。

このため、java-app005 では、src/main/resources/application.properties の内容は次のようになっています。

src/main/resources/application.properties
spring.profiles.active=${profile}

tasks.withType(BootRun) は、bootRun タスク実行時に、--spring.profiles.active 引数として上記の profile 変数の値を渡すようにするというものです。

if (project.hasProperty('debug')) は、-Pdebug というプロパティが指定された場合に、リモートデバッグ用の JVM 引数を設定するというものです。

tasks.withType(BootJar) は、build タスクで実行可能な JAR ファイルを生成する際に、その JAR のマニフェストファイルに有効なプロファイルを記録するというものです。

ビルドと実行(gradlew

このプロジェクトでは、Gradle のプロパティ機能を利用して、環境に応じたビルドや実行が可能です。

ビルド(gradlew

dev プロファイルでのビルド (デフォルト):

次のコマンドを実行すると、dev プロファイルが有効な状態でアプリがビルドされます。

./gradlew build

prod プロファイルでのビルド:

本番環境向けのビルドを行うには、-Pprofile=prod プロパティを指定します。これにより、Spring Boot の prod プロファイルが有効化された JAR ファイルが生成されます。ここでは、確実にプロファイルが適用されるように、clean をしてから build するように指定しています。

./gradlew clean build -Pprofile=prod

実行(gradlew

dev プロファイルでの実行 (デフォルト):

-P オプションを指定しない場合、デフォルトで dev プロファイルが有効になります。

./gradlew bootRun

prod プロファイルでの実行:

本番環境用の設定で実行するには、-Pprofile=prod プロパティを指定します。

./gradlew bootRun -Pprofile=prod

debug プロファイルでの実行:

デバッグモードでアプリを起動し、リモートデバッガの接続をポート 5005 で待ち受けるには、-Pdebug プロパティを指定します。

./gradlew bootRun -Pdebug

ドキュメントとレポートの生成(Gradle 版)

Gradle でも、標準タスクでドキュメントなどを生成できます。

テストを実行するには、次のコマンドを実行します。

./gradlew test

これで、レポートが build/reports/tests/test/index.html に生成されます。

Javadoc を生成するには、次のコマンドを実行します。

./gradlew javadoc

これで、Java API ドキュメントが build/docs/javadoc/ に生成されます。

ビルドツールの比較まとめ

Maven と Gradle は、Java のエコシステムにおける 2 大ビルドツールです。基本的なタスクの対応関係は以下のようになります。

目的 Maven (./mvnw) Gradle (./gradlew)
ビルド(テスト含む) clean install build
アプリ実行 spring-boot:run bootRun
テストのみ実行 test test
スタイルチェック checkstyle:check check
Javadoc 生成 javadoc:javadoc javadoc
依存関係の表示 dependency:tree dependencies

一般的に、Gradle はビルドスクリプトの柔軟性やパフォーマンス(強力なキャッシュ機能)の点で優れていると言われる一方、Maven はその規約の強さと長い歴史から、よりシンプルで安定したプロジェクト管理が可能という特徴があります。

ワークスペースファイル

java-app004java-app005 には、それぞれ java-app004.code-workspacejava-app005.code-workspace という VS Code のワークスペース設定ファイルが含まれています。これにより、プロジェクトごとに最適化された開発環境(設定、タスク、デバッグ構成)を定義できます。

ワークスペースファイルの共通部分

両方のワークスペースファイルには、Java 開発を快適に進めるための共通設定が含まれています。

{
  "settings": {
    // JavaのランタイムとしてDev Container内のJDK 21を指定
    "java.configuration.runtimes": [
      {
        "name": "JavaSE-21",
        "path": "/usr/local/sdkman/candidates/java/21.0.7-tem",
        "default": true
      }
    ],
    // フォーマッターやCheckstyleの設定
    "editor.formatOnSave": true,
    "java.format.settings.url": "eclipse-java-google-style.xml",
    "java.format.settings.profile": "GoogleStyle",
    "java.checkstyle.configuration": "${workspaceFolder}/google_checks.xml"
    // ... その他
  },
  "extensions": {
    // Java, Spring Boot, Git関連の推奨拡張機能を定義
    "recommendations": [
      "vscjava.vscode-java-pack",
      "vmware.vscode-boot-dev-pack",
      "shengchen.vscode-checkstyle"
      // ...
    ]
  }
}

settings は、JDK のパス、Google Java Style Guide に準拠したフォーマッター、Checkstyle のルールなど、コーディングスタイルと実行環境を統一するための設定です。

extensions は、ワークスペースを開いた際に VS Code が推奨する拡張機能のリストです。Java Extension Pack や Spring Boot Extension Pack などが含まれており、開発に必要な拡張機能が何かわかるようになっています。

ワークスペースファイル(Maven 用の設定)

java-app004.code-workspace には、Maven プロジェクトに特化した設定が含まれています。

java-app004.code-workspace(Maven特有の箇所)
{
  "settings": {
    // Mavenがターミナルで使うJDKをJAVA_HOMEに設定
    "maven.terminal.useJavaHome": true,
    // Mavenプロジェクトの表示形式を階層表示に
    "maven.view": "hierarchical"
  },
  "launch": {
    // "Run dev profile" などのデバッグ構成
    "configurations": [
      {
        "type": "java",
        "name": "Run dev profile",
        "request": "launch",
        "mainClass": "internal.dev.javaapp004.JavaApp004Application",
        "args": "--spring.profiles.active=dev",
        "projectName": "java-app004"
      }
      // ...
    ]
  },
  "tasks": {
    // "Build Maven Project" などのタスク構成
    "tasks": [
      {
        "label": "Build Maven Project",
        "type": "shell",
        "command": "./mvnw clean install",
        // ...
      }\n    ]
  }
}

settings には、Maven 拡張機能の挙動を調整する設定が含まれています。

launch には、devprod といった Spring Boot のプロファイルを切り替えてアプリケーションを起動するためのデバッグ構成が定義されています。

tasks には、./mvnw clean install のような Maven コマンドを、VS Code のタスクとして簡単に実行できるように定義されています。

ワークスペースファイル(Gradle 用の設定)

java-app005.code-workspace も同様に、Gradle プロジェクトに特化した設定が含まれています。

java-app005.code-workspace(Gradle特有の箇所)
{
  "launch": {
    "configurations": [
      {
        "type": "java",
        "name": "Run dev profile",
        "request": "launch",
        "mainClass": "internal.dev.javaapp005.JavaApp005Application",
        "projectName": "java-app005",
        "args": "--spring.profiles.active=dev"
      }
      // ...
    ]
  },
  "tasks": {
    "tasks": [
      {
        "label": "Build Gradle Project",
        "type": "shell",
        "command": "./gradlew build",
        // ...
      }
    ]
  }
}

Maven 用との主な違いは、launchtasks で実行されるコマンドが ./gradlew をベースにしている点です。これにより、VS Code の UI から Gradle のビルドや実行を直感的に操作できます。

まとめ

今回は、Spring Boot を使ったモダンな Java コンソールアプリを、Maven と Gradle という 2 つの異なるビルドツールで構築・管理する方法を見てきました。

まず、VS Code の Dev Container を利用することで、JDK や各種ビルドツール、拡張機能がすべてセットアップされた一貫性のある開発環境を簡単に用意できることを示しました。これにより、環境構築の手間を大幅に削減できます。

次に、Spring Boot を導入することで、自動設定やスターター依存関係によって、ビルド設定の簡素化、依存関係管理の自動化、定型コードの削減といった多くのメリットが得られることを確認しました。これにより、開発者は面倒な設定作業から解放され、アプリケーションの本質的なロジック開発に集中できます。

さらに、Maven と Gradle という Java エコシステムの 2 大ビルドツールについて、それぞれの特徴を比較しました。

  • Maven (java-app004) は、規約に基づいたアプローチと pom.xml での設定が特徴です。<parent><profiles> を活用して、依存関係のバージョン管理やビルドの挙動を制御します。
  • Gradle (java-app005) は、Groovy による柔軟なビルドスクリプトと、バージョンカタログ (libs.versions.toml) を使ったモダンな依存関係管理が特徴です。-P オプションでプロパティを渡すことで、ビルドや実行の挙動を動的に変更します。

どちらのツールもプロファイル機能(Maven の <profiles>、Gradle のプロパティ)と Spring Boot のプロファイルを連携させることで、開発、本番、デバッグといった異なる環境に合わせた設定の切り替えをスマートに行えることを学びました。

最後に、VS Code のワークスペースファイル (.code-workspace) が、プロジェクト固有の設定(JDK のパス、フォーマッター、Checkstyle など)や、ビルド・デバッグ用のタスクと起動構成を定義する上で、いかに強力な役割を果たすかを見てきました。

これまでのシリーズを通して、シンプルな Java プロジェクトから、ビルドツールを使った管理、そして Spring Boot フレームワークの導入へと、プロジェクトが進化していく過程を見てきました。これらの各段階で、Dev Container、ビルドツール、そして VS Code の高度な機能を組み合わせることが、現代の Java 開発をいかに効率的で快適なものにするか、実感いただけたのではないでしょうか。

Discussion