Mavenでライブラリをpom.xmlに書いたのにNoSuchMethodErrorが出るときの原因と対処法

Mavenでライブラリをpom.xmlに書いたのにNoSuchMethodErrorが出るときの原因と対処法

仕事で使っているプロジェクト管理ツールがMavenで、
pom.xmlに追加したライブラリを呼び出すとNoSuchMethodErrorや、NoClass系のエラーが出るという事象に遭遇した。
かなり詰まって時間がかかってしまったので、その原因と対処法をナレッジとして残しておく。

原因

原因としては、NoSuchMethodErrorが出ているライブラリのversionがダウングレードしていることだった。
なぜこんなことが起きるのかというと、仕事で使っているようなpomの場合、
大抵古いライブラリ(A)が使用されていて、そこに紐づく別のライブラリ(B)等も一緒に読み込まれている
そこに、新しいライブラリ(C)を使いたくなり追記したのだが、新しいライブラリCでは、ライブラリBの最新版(B`)が必要になっている。
しかし、Mavenでは古い方のライブラリを先にロードしてしまっていたようで、
ライブラリCからライブラリBを読み込んでしまい、「そんなメソッドはない(まだ対応されていないversion)」というエラーが出てしまうようである。

文章だけだと分かりにくいので、以下に図で示した。

理想
本来はこう呼び出して欲しい
現実
しかし、先にロードした方を見に行く。

原因の調べ方

ちなみに、この原因に辿り着く方法だが、依存関係を表示するコマンドを使用すればいい。
まず、古いライブラリの混じっていない新規プロジェクトを作成し、
今回使いたいライブラリが問題なく動作することを確認したうえで、
mvn dependency:tree」コマンド等を使って依存関係を確認する。

その後、古いライブラリの混じっているプロジェクトでも同じように依存関係を確認するコマンドを使用し、
上記で確認した依存関係と比較して、ダウングレードしているものが存在しないか確認する、といった手順である。

ちなみに、今回は新しいライブラリの方が使えなくなったパターンを紹介したが、
クラスがロードされるタイミングによっては、
新しい方を読み込んでしまって古い方が使えなくなるというパターンもありそうなので注意。

対処法

原因が今回のようなダウングレードであった場合、pom.xmlを編集することで対応できる。
普通pomを編集してライブラリを追加する場合、以下の様に記載すると思うが、
この「version」の下に「exclusions」というのを追加することで対応可能。

<dependency>
    <groupId>hoge</groupId>
    <artifactId>fuga</artifactId>
    <version>1.0.0</version>
    <exclusions>
        <exclusion>
            <artifactId>*</artifactId>
            <groupId>hogehoge</groupId>
        </exclusion>
    </exclusions>
</dependency>

「exclusions」の指定をすることで、上記の場合、
hogeは欲しいけど、その依存関係であるhogehogeはexclude(除外する)」という指定になります。
そうすると、再びhogeの依存関係にある最新版のライブラリを読みに行ってくれる、ということだそうです。

まとめ

以上、Mavenで読み込んだライブラリがNoSuchMethodErrorを吐いた時の原因と対処法をまとめました。
実際には自力で解けたわけではなく、以下のようなサイトを見つけて対応しました。
その過程で、今回のような原因意外にも、ライブラリのコンフリクト原因(ただ、これはmaven installすると警告されるのでそれで分かるはず)等も見つけたので、
必ずしもすべてのNoSuchMethodErrorがこれで解決するわけではありませんが、一つの助けになれば幸いです。

参考サイト

Appendix Maven2はまり道
http://www.nulab.co.jp/kousei/chapter7/06.html
jasperreportをMavenの依存関係に追加したら、jerseyがJSONを読めなくなった
https://qiita.com/suzuq/items/c49e1339f8cc7df40d1e