Gradle に Java ライブラリを追加すると cannot be resolved と怒られた

build.gradledependenciesSpring Boot Starter Validation を追加した。

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}

これを import jakarta.validation.constraints.NotBlank; してみると依存を解決できないと怒られた。

The import jakarta.validation cannot be resolvedJava(268435846)

Spring Boot Starter Validation を追加すると、jakarta.validation.constraints.XX が利用できるようだが、私の環境では上手く利用できないので調べてみる。

仮説

Spring Boot Starter Validation が Jakarta Bean Validation に依存しているから、私たち利用する側は明示的に追加する必要がないものだと勝手思っている。

であれば、追加したのに依存が解決されていない原因は大きく分けると以下の 2 つであると考えた。

  1. プロジェクトに正しくライブラリを追加できていない
  2. エディタがライブラリを認識できていない

検証

1 から順に確認する。

1. プロジェクトに正しくライブラリを追加できていない

依存をリフレッシュするコマンドを実行した。

./gradlew build --refresh-dependencies

この後、以下のコマンドで依存関係を表示してみた。

./gradlew dependencies 

すると、Spring Boot と同じバージョンの org.springframework.boot:spring-boot-starter-validation -> 3.0.2 がツリーの中に表示されいたので、おそらく正しくプロジェクトには追加されているのだろう。

また、仮説の通り Jakarta Bean Validation と思われるライブラリも表示されている。

compileClasspath - Compile classpath for source set 'main'.
| # ..省略
\--- org.springframework.boot:spring-boot-starter-validation -> 3.0.2
     +--- org.springframework.boot:spring-boot-starter:3.0.2 (*)
     +--- org.apache.tomcat.embed:tomcat-embed-el:10.1.5
     \--- org.hibernate.validator:hibernate-validator:8.0.0.Final
          +--- jakarta.validation:jakarta.validation-api:3.0.2
          +--- org.jboss.logging:jboss-logging:3.4.1.Final -> 3.5.0.Final
          \--- com.fasterxml:classmate:1.5.1

ここで追加したライブラリはどこに追加されているのか気になったので調べてみた。

Gradle の公式ドキュメントを参照すると、$USER_HOME/.gradlemodules-2 に依存ライブラリが格納されていると書かれている。

├── caches 
│   ├── 4.8 
│   ├── 4.9 
│   ├── ⋮
│   ├── jars-3 
│   └── modules-2 # <-

docs.gradle.org

私は Docker の中に開発環境を構築していたので、Docker Container に接続して確認してみると、確かにライブラリが追加されていた。

ls ~/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-validation/
# 3.0.2

プロジェクトにライブラリは追加されている様だ。

2. エディタがライブラリを認識できていない

VSCode拡張機能 Extension Pack for Java によって表示されている JAVA PROJECTS の Project and External Dependencies を見てみると、パスがローカル PC を指していることに気付いた。

ソースコードを Docker Container にバインドマウントした状態で、Docker Container に接続して Gradle のコマンドを実行していたので、あたかも Docker 内で開発している気分だったが、VSCode は Container には接続していないので当然 Conainer の中のライブラリは参照できなかったのだ。

今まではローカル PC に同じバージョンの Spring Boot 等のライブラリがたまたま存在していたから、VSCode 上でも補完が効いていたのだろうか。そして今回ローカル PC に存在しないライブラリを追加したタイミングで初めて問題になったのだろうか。

解決

原因が分かったので VSCode を Docker Container に接続するために、今まで存在は知っていたが必要ないだろうと思っていた VSCode Dev Containers を設定することにした。

code.visualstudio.com

docker-compose.ymlDockerfile をそのまま利用できてすぐに環境を作ることができた。

とりあえず最小限の設定を書いた。

// .devcontainer/devcontainer.json
{
    "name": "plantree-dev-container",
    "dockerComposeFile": [
        "../docker-compose.yml"
    ],
    "service": "workspace",
    "remoteUser": "devuser",
    "workspaceFolder": "/home/devuser/workspace",
    "customizations": {
        "vscode": {
            "extensions": [
                "vscjava.vscode-java-pack",
                "vscjava.vscode-gradle"
            ]
        }
    }
}

Dev Container を起動した状態で JAVA PROJECTS とやらを確認すると、パスが Docker Container の中のライブラリを指していた。また、ローカル PC には無かった Spring Boot Starter Validation を認識していた。

エディタ上で jakarta.validation.constraints.NotBlank を import することができた。さらに、Dev Container を利用することで、Gradle のタスクを VSCode から実行できるようになった。すごく便利だ。

かなり初歩的なことが原因だったが、Java や Gradle について分からないことが多いので問題の切り分けに時間がかかってしまった。