サンプルコードで身につける: Maven: 依存解決(推移依存・nearest wins・dependencyManagement・exclusions・スコープ)
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1Boot 雛形の dependency:tree 全文(依存2本 → 72本)
に書いた直接の依存は と spring-boot-starter-webmvc-test の2本だけです。木にすると依存は72本(compile 39、test 33)になり、強調した枝が最も深い5段です。末尾の :compile や :test はで、runtime は0本でした。
[INFO] com.example:demo:jar:0.0.1-SNAPSHOT[INFO] +- org.springframework.boot:spring-boot-starter-webmvc:jar:4.1.1:compile[INFO] | +- org.springframework.boot:spring-boot-starter:jar:4.1.1:compile[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:4.1.1:compile[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.5.38:compile[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.5.38:compile[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.25.5:compile[INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.25.5:compile[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:2.0.18:compile[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:4.1.1:compile[INFO] | | +- jakarta.annotation:jakarta.annotation-api:jar:3.0.0:compile[INFO] | | \- org.yaml:snakeyaml:jar:2.6:compile[INFO] | +- org.springframework.boot:spring-boot-starter-jackson:jar:4.1.1:compile[INFO] | | \- org.springframework.boot:spring-boot-jackson:jar:4.1.1:compile[INFO] | | \- tools.jackson.core:jackson-databind:jar:3.1.5:compile[INFO] | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.21:compile[INFO] | | \- tools.jackson.core:jackson-core:jar:3.1.5:compile[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:4.1.1:compile[INFO] | | +- org.springframework.boot:spring-boot-starter-tomcat-runtime:jar:4.1.1:compile[INFO] | | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:11.0.24:compile[INFO] | | | +- org.apache.tomcat.embed:tomcat-embed-el:jar:11.0.24:compile[INFO] | | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:11.0.24:compile[INFO] | | \- org.springframework.boot:spring-boot-tomcat:jar:4.1.1:compile[INFO] | +- org.springframework.boot:spring-boot-http-converter:jar:4.1.1:compile[INFO] | | +- org.springframework.boot:spring-boot:jar:4.1.1:compile[INFO] | | | \- org.springframework:spring-context:jar:7.0.9:compile[INFO] | | \- org.springframework:spring-web:jar:7.0.9:compile[INFO] | | +- org.springframework:spring-beans:jar:7.0.9:compile[INFO] | | \- io.micrometer:micrometer-observation:jar:1.17.1:compile[INFO] | | \- io.micrometer:micrometer-commons:jar:1.17.1:compile[INFO] | \- org.springframework.boot:spring-boot-webmvc:jar:4.1.1:compile[INFO] | +- org.springframework.boot:spring-boot-servlet:jar:4.1.1:compile[INFO] | \- org.springframework:spring-webmvc:jar:7.0.9:compile[INFO] | +- org.springframework:spring-aop:jar:7.0.9:compile[INFO] | \- org.springframework:spring-expression:jar:7.0.9:compile[INFO] \- org.springframework.boot:spring-boot-starter-webmvc-test:jar:4.1.1:test[INFO] +- org.springframework.boot:spring-boot-starter-jackson-test:jar:4.1.1:test[INFO] +- org.springframework.boot:spring-boot-starter-test:jar:4.1.1:test[INFO] | +- org.springframework.boot:spring-boot-test:jar:4.1.1:test[INFO] | +- org.springframework.boot:spring-boot-test-autoconfigure:jar:4.1.1:test[INFO] | +- com.jayway.jsonpath:json-path:jar:2.10.0:test[INFO] | | \- org.slf4j:slf4j-api:jar:2.0.18:compile[INFO] | +- jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.5:test[INFO] | | \- jakarta.activation:jakarta.activation-api:jar:2.1.4:test[INFO] | +- net.minidev:json-smart:jar:2.6.0:test[INFO] | | \- net.minidev:accessors-smart:jar:2.6.0:test[INFO] | | \- org.ow2.asm:asm:jar:9.7.1:test[INFO] | +- org.assertj:assertj-core:jar:3.27.7:test[INFO] | | \- net.bytebuddy:byte-buddy:jar:1.18.11:test[INFO] | +- org.awaitility:awaitility:jar:4.3.0:test[INFO] | +- org.hamcrest:hamcrest:jar:3.0:test[INFO] | +- org.junit.jupiter:junit-jupiter:jar:6.0.3:test[INFO] | | +- org.junit.jupiter:junit-jupiter-api:jar:6.0.3:test[INFO] | | | +- org.opentest4j:opentest4j:jar:1.3.0:test[INFO] | | | +- org.junit.platform:junit-platform-commons:jar:6.0.3:test[INFO] | | | \- org.apiguardian:apiguardian-api:jar:1.1.2:test[INFO] | | +- org.junit.jupiter:junit-jupiter-params:jar:6.0.3:test[INFO] | | \- org.junit.jupiter:junit-jupiter-engine:jar:6.0.3:test[INFO] | | \- org.junit.platform:junit-platform-engine:jar:6.0.3:test[INFO] | +- org.mockito:mockito-core:jar:5.23.0:test[INFO] | | +- net.bytebuddy:byte-buddy-agent:jar:1.18.11:test[INFO] | | \- org.objenesis:objenesis:jar:3.3:test[INFO] | +- org.mockito:mockito-junit-jupiter:jar:5.23.0:test[INFO] | +- org.skyscreamer:jsonassert:jar:1.5.3:test[INFO] | | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test[INFO] | +- org.springframework:spring-core:jar:7.0.9:compile[INFO] | | +- commons-logging:commons-logging:jar:1.3.6:compile[INFO] | | \- org.jspecify:jspecify:jar:1.0.1:compile[INFO] | +- org.springframework:spring-test:jar:7.0.9:test[INFO] | \- org.xmlunit:xmlunit-core:jar:2.11.0:test[INFO] +- org.springframework.boot:spring-boot-webmvc-test:jar:4.1.1:test[INFO] | \- org.springframework.boot:spring-boot-web-server:jar:4.1.1:compile[INFO] \- org.springframework.boot:spring-boot-resttestclient:jar:4.1.1:test
2nearest wins を再現する最小の pom.xml(24行)
直接の依存として古い 3.12.0 を書き(強調の前半)、さらに commons-text 1.15.0 を書きます(後半)。commons-text 1.15.0 の は commons-lang3 3.20.0 を要求しているので、同じライブラリに2つの経路で出会います。 を使わないので、版は自分で全部書いています。
<project xmlns="http://maven.apache.org/POM/4.0.0"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>nearest</artifactId><version>1.0.0</version><properties><maven.compiler.release>25</maven.compiler.release><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- direct: an OLD commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><!-- commons-text 1.15.0 itself depends on commons-lang3 3.20.0 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-text</artifactId><version>1.15.0</version></dependency></dependencies></project>
34つの pom の dependency:tree: nearest・直接宣言なし・dependencyManagement・exclusions
上の pom と、それを3通りに変えた pom の木です。nearest では を付けると、3.20.0 が omitted for conflict with 3.12.0 と表示されます。直接の宣言を消すと書いていない 3.20.0 が乗り、 に 3.21.0 を書くと version managed from 3.20.0 と出て置き換わります。 では commons-text の下の枝が消えます。
$ mvn dependency:tree # nearest[INFO] com.example:nearest:jar:1.0.0[INFO] +- org.apache.commons:commons-lang3:jar:3.12.0:compile[INFO] \- org.apache.commons:commons-text:jar:1.15.0:compile$ mvn dependency:tree -Dverbose[INFO] com.example:nearest:jar:1.0.0[INFO] +- org.apache.commons:commons-lang3:jar:3.12.0:compile[INFO] \- org.apache.commons:commons-text:jar:1.15.0:compile[INFO] \- (org.apache.commons:commons-lang3:jar:3.20.0:compile - omitted for conflict with 3.12.0)# transitive: 12〜16 行目(直接の commons-lang3)を消した pom$ mvn dependency:tree[INFO] com.example:transitive:jar:1.0.0[INFO] \- org.apache.commons:commons-text:jar:1.15.0:compile[INFO] \- org.apache.commons:commons-lang3:jar:3.20.0:compile# managed: 直接の commons-lang3 を消し、<dependencies> の前に次を足した pom# <dependencyManagement><dependencies><dependency># <groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.21.0</version># </dependency></dependencies></dependencyManagement>$ mvn dependency:tree -Dverbose[INFO] com.example:managed:jar:1.0.0[INFO] \- org.apache.commons:commons-text:jar:1.15.0:compile[INFO] \- org.apache.commons:commons-lang3:jar:3.21.0:compile (version managed from 3.20.0)# excl: commons-text の <version> の後に次を足した pom# <exclusions><exclusion># <groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId># </exclusion></exclusions>$ mvn dependency:tree[INFO] com.example:excl:jar:1.0.0[INFO] +- org.apache.commons:commons-lang3:jar:3.12.0:compile[INFO] \- org.apache.commons:commons-text:jar:1.15.0:compile# nearest をビルドして実行(この組み合わせでは 3.12.0 でも動いた。常に動くとは言えない)$ mvn -q package && java -cp target/nearest-1.0.0.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout) com.example.MainHello From Commons-text
4誤解しやすい出力: dependency:analyze はスターターを「未使用」と言う
は、コードが実際に使っているの出どころと pom の宣言を突き合わせます。 の雛形に掛けると、宣言した2本のスターターは Unused declared(書いているのに使っていない)、 や spring-web は Used undeclared(使っているのに書いていない)と出ます。スターターは依存の束で自分ではクラスを持たないからで、この警告どおりに pom を直すとスターターの意味が無くなります。
$ mvn dependency:analyze[INFO] --- dependency:3.10.0:analyze (default-cli) @ demo ---[WARNING] Used undeclared dependencies found:[WARNING] org.springframework:spring-context:jar:7.0.9:compile[WARNING] org.junit.jupiter:junit-jupiter-api:jar:6.0.3:test[WARNING] org.springframework.boot:spring-boot:jar:4.1.1:compile[WARNING] org.springframework:spring-web:jar:7.0.9:compile[WARNING] org.springframework.boot:spring-boot-test:jar:4.1.1:test[WARNING] org.springframework.boot:spring-boot-autoconfigure:jar:4.1.1:compile[WARNING] Unused declared dependencies found:[WARNING] org.springframework.boot:spring-boot-starter-webmvc:jar:4.1.1:compile[WARNING] org.springframework.boot:spring-boot-starter-webmvc-test:jar:4.1.1:test[INFO] BUILD SUCCESS
5うまくいかない場合: 無い座標と、オフライン(-o)のエラー
には、取ってきた と一緒に出どころの帳簿 が置かれます。存在しないを依存に書くと Could not find artifact で失敗し、取れなかった記録 だけが同じ場所に残ります。(オフライン)では、取得済みのものは普通にビルドでき、無いものは「以前に取得していない」という別のエラーになります。どちらのファイルも形式は Resolver の内部のもので(1行目にそう書いてあります)、版で変わりえます。
$ cat ~/.m2/repository/org/apache/commons/commons-text/1.15.0/_remote.repositories#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice.#Sat Oct 03 00:28:46 UTC 2026commons-text-1.15.0.jar>central=commons-text-1.15.0.pom>central=# 依存に com.example.nowhere:does-not-exist:1.0 を書いた pom$ mvn compile[WARNING] The POM for com.example.nowhere:does-not-exist:jar:1.0 is missing, no dependency information available[ERROR] Failed to execute goal on project missing: Could not resolve dependencies for project com.example:missing:jar:1.0.0[ERROR] dependency: com.example.nowhere:does-not-exist:jar:1.0 (compile)[ERROR] Could not find artifact com.example.nowhere:does-not-exist:jar:1.0 in central (https://repo.maven.apache.org/maven2)$ find ~/.m2/repository/com/example/nowhere -type f.../does-not-exist/1.0/does-not-exist-1.0.jar.lastUpdated.../does-not-exist/1.0/does-not-exist-1.0.pom.lastUpdated$ cat .../does-not-exist-1.0.jar.lastUpdated#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice.#Sat Oct 03 00:29:14 UTC 2026https\://repo.maven.apache.org/maven2/.error=https\://repo.maven.apache.org/maven2/.lastUpdated=1790987354784$ mvn -o -DskipTests package # Boot 雛形。依存は取得済み[INFO] --- resources:3.5.0:resources (default-resources) @ demo ---...[INFO] --- spring-boot:4.1.1:repackage (repackage) @ demo ---[INFO] BUILD SUCCESS$ mvn -o compile # 無い座標を書いた pom[ERROR] Cannot access central (https://repo.maven.apache.org/maven2) in offline mode and the artifact com.example.nowhere:does-not-exist:jar:1.0 has not been downloaded from it before.