🐘

Gradle Plugins の基本を Spring Boot で学ぶ

2025/02/09に公開

はじめに

前回からの3回目です。

これまでの学習は以下です。

  1. Gradle Projects の基本を Spring Boot で学ぶ
  2. Gradle Tasks の基本を Spring Boot で学ぶ

本稿は Plugins が対象です。

Plugins のタイプなどの概念、基本的な Plugin の使い方を実践します。
Core plugin、 Convention plugin 、Version Catalog を使ってみます。

[注]

筆者は Java に精通しているわけでも SpringBoot に精通しているわけでもありません。

現在進行形で学習しており、その内容の理解のためにブログでまとめています。
なお、IDEはVSCodeです。

Plugins とは

再利用性の高い一般的なビルドタスクや、外部ツールやサービスとの統合や特定プロジェクトのニーズに合わせたビルドプロセスのカスタマイズにも利用できます。

特定用途のために編成されたTask群というべきものと自分は理解しています。

なお、 Plugins と Dependencies の違いは、対象の違いと理解しています。

  • Plugins: ビルドプロセスに関連する機能を拡張する
  • Dependencies: アプリケーションが利用するためのライブラリを提供

Plugins は概念が多く(ドキュメントも散らばってる感あり)、Gradleのヤマの一つだな、と感じました。

Plugins の概要として、おさえるべき4つの概念が以下です。

Plugins でおさえるべき4つの概念

  1. Plugins の3つの配布元
  2. Plugins の3つのスコープ
  3. Plugins の4つの実装方法
  4. Plugins の解決と適用の選択肢

それぞれ順に見ていきます。

1 Plugins の3つの配布元

Plugin をどこから取得できるか、の3パターンです。

種類 説明 適用方法 バージョン指定
Core plugins Gradle 自体に組み込まれているプラグイン • java
• groovy
• application
• ear
plugins { id 'java' } 不要(Gradleのバージョンに依存)
Community plugins Gradle Plugin Portal で公開されているプラグイン • org.springframework.boot
• io.spring.dependency-management
plugins { id 'org.springframework.boot' version '3.1.5' } 必要
Local plugins 特定のプロジェクトや組織内で定義するカスタムプラグイン • buildSrc内のプラグイン
• スクリプトプラグイン
plugins { id 'custom-plugin' } プロジェクト内で管理

Plugins をどこから取得できるかを把握したので、使い方です。

Plugins の使い方

Plugin は解決(取得 → 読み込み)して、対象に適用する必要があります。

以下の例では、 Plugin を自動的に読み込んで適用しています。

Plugin DSL の詳細については、PluginDependenciesSpec で参照できます。

// コアプラグインはバージョン情報が不要
plugins {
    id("java")
}

// 非コアプラグインはバージョン情報が必要
plugins {
	id 'org.springframework.boot' version '3.4.2'
}

具体的に、Core plugin を使ってみます。

SpringBoot プロジェクトで Core pluginを確認してみる

SpringBoot のGradle プロジェクトは既に前回から作成済みの想定です。

Core plugins から The Checkstyle Plugin を適用してみます。

plugins {
	id 'java'
	id 'checkstyle' // 追加
	id 'org.springframework.boot' version '3.4.2'
	id 'io.spring.dependency-management' version '1.1.7'
}

追加された Tasks を確認してみます。

./gradlew tasks --group="other" --all

------------------------------------------------------------
Tasks runnable from root project 'demo'
------------------------------------------------------------

Other tasks
-----------
checkstyleMain - Run Checkstyle analysis for main classes
checkstyleTest - Run Checkstyle analysis for test classes
compileJava - Compiles main Java source.
compileTestJava - Compiles test Java source.
components - Displays the components produced by root project 'demo'. [deprecated]
dependentComponents - Displays the dependent components of components in root project 'demo'. [deprecated]
model - Displays the configuration model of root project 'demo'. [deprecated]
prepareKotlinBuildScriptModel
processResources - Processes main resources.
processTestResources - Processes test resources.

Tasks に記載されている、checkstyleMaincheckstyleTestcheckstyleSourceSet が表示されるかと思いきや、Java の plugin Source set を設定していないため、前2つしか表示されないようです。
(慣れの問題かも知れないですが、ドキュメント読みづらい…)

Project layout にファイル配置しろ、と記載されているので場所とファイルを作成します。

mkdir -p config/checkstyle && \\
touch config/checkstyle/checkstyle.xml

で、そのチェックスタイルの中身どこにあるねん、と戸惑いましたが、checkstyle の Configuration の末尾 Configuration XML Structure に例で sun_checks.xmlgoogle_checks.xml が挙げられているので、どっちでも良いので貼り付けて利用します。

./gradlew checkstyleMain
> Task :checkstyleMain FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':checkstyleMain'.
> A failure occurred while executing org.gradle.api.plugins.quality.internal.CheckstyleAction
   > An unexpected error occurred configuring and executing Checkstyle.
      > Unable to create Root Module: config

…え、何かコケました。

詳細出力して再実行します。

> ./gradlew checkstyleMain --stacktrace
> Task :checkstyleMain FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':checkstyleMain'.
> A failure occurred while executing org.gradle.api.plugins.quality.internal.CheckstyleAction
   > An unexpected error occurred configuring and executing Checkstyle.
      > Unable to create Root Module: config
      
〜超絶長いので省略〜
Caused by: com.puppycrawl.tools.checkstyle.api.CheckstyleException: 'SuppressWithNearbyTextFilter' クラスをインスタンス化できませんでした。また、以下のクラス名でもインスタンス化できませんでした .SuppressWithNearbyTextFilter, SuppressWithNearbyTextFilterCheck, .SuppressWithNearbyTextFilterCheck 。 クラス名を正規名で指定しているか再確認してください。または、単純名の使用を設定する方法 https://checkstyle.org/config.html#Packages をお読みください。 また、Checker に渡されているクラスローダが正しく設定されているか再確認してください。
        at com.puppycrawl.tools.checkstyle.PackageObjectFactory.createModule(PackageObjectFactory.java:215)
        at com.puppycrawl.tools.checkstyle.Checker.setupChild(Checker.java:466)
        ... 42 more

あぁなるほどね!

…まったく分からんのでググると、まったく同じような状況の GradleでCheckStyleを利用する な方を見つけたので、同じように gradle のバージョンを自分も確認します。

❯ ./gradlew dependencies | grep "checkstyle:"
\--- com.puppycrawl.tools:checkstyle:9.3

で、SuppressWithNearbyTextFilter

Checkstyleのリリースノート で確認すると、 10.10.0 で突っ込まれたようです。

つまり、 10.10.0 以上のバージョンじゃないとダメなので build.gradle に追記します。

// 2025.02.09の最新版
checkstyle {
    toolVersion = "10.21.2"
}

再度実行します。

./gradlew checkstyleMain                                                                     

> Task :checkstyleMain
[ant:checkstyle] [WARN] /spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:1:9: Package name 'com.example.demo' must match pattern '^[a-z]+(\.[a-z][a-z0-9]*)*$'. [PackageName]
[ant:checkstyle] [WARN] /spring-boot-gradle/spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:6:1: Javadoc コメントがありません。 [MissingJavadocType]
[ant:checkstyle] [WARN] /spring-boot-gradle/spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:9:1: 行にタブ文字が含まれています。 [FileTabCharacter]
[ant:checkstyle] [WARN] /spring-boot-gradle/spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:9:9: 'method def modifier' のインデントレベル 8 は正しくありません。期待されるレベルは 2 です。 [Indentation]
[ant:checkstyle] [WARN] /spring-boot-gradle/spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:9:9: Javadoc コメントがありません。 [MissingJavadocMethod]
[ant:checkstyle] [WARN] /spring-boot-gradle/spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:10:1: 行にタブ文字が含まれています。 [FileTabCharacter]
[ant:checkstyle] [WARN] /spring-boot-gradle/spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:10:17: 'method def' の子のインデントレベル 16 は正しくありません。期待されるレベルは 4 です。 [Indentation]
[ant:checkstyle] [WARN] /spring-boot-gradle/spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:11:1: 行にタブ文字が含まれています。 [FileTabCharacter]
[ant:checkstyle] [WARN] /spring-boot-gradle/spring-boot-gradle/demo/src/main/java/com/example/demo/DemoApplication.java:11:9: 'method def rcurly' のインデントレベル 8 は正しくありません。期待されるレベルは 2 です。 [Indentation]
Checkstyle rule violations were found. See the report at: file:////spring-boot-gradle/demo/build/reports/checkstyle/main.html
Checkstyle files with violations: 1
Checkstyle violations by severity: [warning:9]

BUILD SUCCESSFUL in 4s
5 actionable tasks: 1 executed, 4 up-to-date

動きました!

すんなり動くと思っていたのに、そこそこ手こずったのでこれだけで嬉しいです。

2 Plugins の3つのスコープ

次に Plugin の3つのスコープです。

上の例は、 Project Plugin になります。

プラグインタイプ 適用場所 スコープ 主な用途
Project Plugin build.gradle / build.gradle.kts 特定プロジェクト - ビルドロジックのカスタマイズ
- タスクの追加
- プロジェクト固有の設定
Settings Plugin settings.gradle / settings.gradle.kts ビルド全体 - プロジェクトの包含定義
- ビルドスクリプトリポジトリの設定
- 全プロジェクトの共通設定
Init Plugin init.gradle / init.gradle.kts Gradleビルド(グローバル) - Gradleバージョンの設定
- デフォルトリポジトリの設定
- 共通プラグインの全ビルドへの適用

以下の3, 4は、それぞれ Plugin の実装方法と適用についてのまとめです。

ほぼ公式の転載です。

3 Plugins の4つの実装方法

Plugin のタイプ 実装方法 適用例 備考
Script plugins Groovy/Kotlin DSLを build.gradle に直接記述 apply<HelloWorldPlugin>() 非推奨。ソースコードで共有。実行毎のコンパイル。
Precompiled script plugins Groovy/Kotlin DSLをコンパイルしてJavaクラスファイルとして配布 plugins {id("my-plugin") version "1.0"} JVMバイトコードにコンパイルされて共有。
BuildSrc and Convention plugins Precompiled と Binary のハイブリッド Plugin。buildSrc 内で記述されたJava/Groovy/Kotlin (DSL)でコンパイルして配布 plugins {id("shared-build-conventions")} 複雑な処理も記述できる。JVMバイトコードにコンパイルされて共有。
Binary plugins Java/Kotlin(DSL) をコンパイルして配布 plugins {id("my-plugin") version "1.0"} JVMバイトコードにコンパイルされて共有。

4 Plugin の解決と適用の選択肢

Resolving plugins を参照。

プロジェクトタイプ(ソロプロジェクトまたはマルチプロジェクト)への Plugin 解決と適用のアプローチと推奨などをまとめています。

Convention Plugin でマルチプロジェクトに適用してみる

マルチプロジェクトでの plugin 適用するのに推奨されている convention plugin を対応してみます。

下記のような構成にします。

.
├── buildSrc
│   ├── build.gradle
│   └── src
│       └── main
│           └── groovy
│               └── demo-conventions.gradle
├── hoge
│   ├── build.gradle
│   └── src
│       ├── main
│       │   ├── java
│       │   └── resources
│       └── test
│           ├── java
│           └── resources
├── fuga
│   ├── build.gradle
│   └── src
│       ├── main
│       │   ├── java
│       │   └── resources
│       └── test
│           ├── java
│           └── resources
├── settings.gradle
└── build.gradle
# ディレクトリ構造の作成
mkdir -p buildSrc/src/main/groovy
mkdir -p hoge/src/{main,test}/{java,resources}
mkdir -p fuga/src/{main,test}/{java,resources}

# 必要なファイルを作成
touch buildSrc/build.gradle
touch buildSrc/src/main/groovy/demo-conventions.gradle
touch hoge/build.gradle
touch fuga/build.gradle
// settings.gradle
rootProject.name = 'demo'
// subProject を追加
include 'hoge', 'fuga'

root の build.gradle にあった内容を demo-conventions.gradle に移します。

// buildSrc/src/main/groovy/demo-conventions.gradle
plugins {
    id 'java'
    id 'checkstyle'
    id 'org.springframework.boot'
    id 'io.spring.dependency-management'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

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

repositories {
    mavenCentral()
}

checkstyle {
    toolVersion = "10.21.2"
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform()
}

ただし、Applying external plugins に記載例がありますが、外部プラグインを Precompiled script plugin に適用する場合、バージョンは記載してはいけないようなので、バージョン情報は buildSrc/build.gradle に記述します。

また、 Precompiled script plugins に記載されるように、Precompiled script plugin を作るために、 groovy-gradle-plugin が必要になります。 (.kts なら kotlin-dsl)

// buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-gradle-plugin:3.4.2'
}

その他の build.gradle は以下です。

// ./build.gradle
plugins {
    id("demo-conventions")
}

// hoge/build.gradle
plugins {
    id("demo-conventions")
}

// fuga/build.gradle
plugins {
    id("demo-conventions")
}

マルチプロジェクトになっているか確認します。

./gradlew projects
Starting a Gradle Daemon, 1 busy and 1 stopped Daemons could not be reused, use --status for details

> Task :projects

Projects:

------------------------------------------------------------
Root project 'demo'
------------------------------------------------------------

Root project 'demo'
+--- Project ':fuga'
\--- Project ':hoge'

To see a list of the tasks of a project, run gradlew <project-path>:tasks
For example, try running gradlew :fuga:tasks

ちゃんとそれぞれのプロジェクトに一括で設定されているかを確認します。

// hoge./gradlew :hoge:tasks --group="other" --all       

> Task :hoge:tasks

------------------------------------------------------------
Tasks runnable from project ':hoge'
------------------------------------------------------------

Tasks
-----
checkstyleMain - Run Checkstyle analysis for main classes
checkstyleTest - Run Checkstyle analysis for test classes
compileJava - Compiles main Java source.
compileTestJava - Compiles test Java source.
components - Displays the components produced by project ':hoge'. [deprecated]
dependentComponents - Displays the dependent components of components in project ':hoge'. [deprecated]
model - Displays the configuration model of project ':hoge'. [deprecated]
processResources - Processes main resources.
processTestResources - Processes test resources.

BUILD SUCCESSFUL in 674ms
8 actionable tasks: 1 executed, 7 up-to-date

// fuga./gradlew :fuga:tasks --group="other" --all       

> Task :fuga:tasks

------------------------------------------------------------
Tasks runnable from project ':fuga'
------------------------------------------------------------

Tasks
-----
checkstyleMain - Run Checkstyle analysis for main classes
checkstyleTest - Run Checkstyle analysis for test classes
compileJava - Compiles main Java source.
compileTestJava - Compiles test Java source.
components - Displays the components produced by project ':fuga'. [deprecated]
dependentComponents - Displays the dependent components of components in project ':fuga'. [deprecated]
model - Displays the configuration model of project ':fuga'. [deprecated]
processResources - Processes main resources.
processTestResources - Processes test resources.

BUILD SUCCESSFUL in 638ms
8 actionable tasks: 1 executed, 7 up-to-date

うまくできていそうです。

続けて、 Version Catalog を導入してみます。

Version Catalog を使ってみる

version Catalog とは

以下のような利点を持ちます。

(Plugin Management という Plugins に特化した一元管理機能もあります)

  • ライブラリ・プラグインのバージョン管理を一元化
  • プロジェクト全体で一貫性を保てる
  • IDE のサポート(補完が効くなど)

まずは、 libs.versions.toml を作ります。

libs.versions.toml は依存関係の宣言をファイル内で一元管理します。

記載の詳細は、 Version Catalog を参照下さい。

// gradle/libs.versions.toml
[versions]
spring-boot = "3.4.2"
spring-dependency = "1.1.7"
checkstyle = "10.21.2"

[libraries]
spring-boot-starter-web = { group = "org.springframework.boot", name = "spring-boot-starter-web" }
spring-boot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test" }
junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
spring-boot-gradle-plugin = { group = "org.springframework.boot", name = "spring-boot-gradle-plugin", version.ref = "spring-boot" }

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

次に、ルートの setting.gradle です。

// ./setting.gradle
dependencyResolutionManagement {} // 追加
rootProject.name = 'demo'
include 'hoge', 'fuga'

Importing a catalog from a file]に、Remember that you don’t need to import the version catalog named libs.versions.toml if it resides in your gradle folder. It will be imported automatically. というように記載されています。

buildSrc 以外では特に必要がなさそうです。

Accessing a catalog を参考に以下のようにしました。

// buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation libs.spring.boot.gradle.plugin
}

が、これではビルドが通りませんでした。

なぜ?とググると

https://discuss.gradle.org/t/cant-access-versioncatalogs-defined-in-settings-gradle-from-buildsrc/42920

のような回答があり、つまり、 buildSrc は root の settings.gradle からも独立して(先に?)ビルドされていると。(ドキュメントに書いてあったっけ…)

なので、 buildSrc の setting.gradle で対応する必要がありそうだと。

で、結局、buildSrc に settings.gradle を配置しても、settings.gradleがversion catalogを読み込むまで build.gradleは libs を認識できないようなため、以下を参考に

https://github.com/gradle/gradle/issues/15383

https://discuss.gradle.org/t/trouble-using-centralized-dependency-versions-in-buildsrc-plugins-and-buildscript-classpath/39421

下記のような設定になりました。

// ./setting.gradle
// dependencyResolutionManagement {} 削除
rootProject.name = 'demo'
include 'hoge', 'fuga'
// buildSrc/settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}
// buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation files(libs.class.superclass.protectionDomain.codeSource.location)
    implementation libs.spring.boot.gradle.plugin
}

Plugins はスムーズにいかないところも多く、なかなか大変でした。

とはいえ、コケることで学べることが多かったのは良かったです。

次は、最後の Dependency Management になります。

参考リンク

Discussion