理系学生日記

おまえはいつまで学生気分なのか

Javaにおける依存関係を可視化できるjarvizを試してみる

モノリス的なJavaアプリケーションにおいて、各クラス・メソッドがどのように依存し合っているのかを見極めたいというのは しばしば生じるニーズです。 アプリケーションが巨大になれば分割したいと考えるのは自然の摂理でもあり、 分割面がどこなのかを見極めることもできる可能性があります。

このような用途で使えるツールはないものかと、Jarvizを試してみました。

Jarvizとは

Jarvizは、あるメソッドが他のメソッドを呼び出すことを依存関係と定義し、その依存関係を解析・可視化するツールです。 依存関係はByte Codeから読み取るので、JarファイルやWarファイルがあれば解析可能です。

このあたりは、以下のTech Blogエントリに詳細な記載があります。

また、Jarviz自体にもMavenが統合されているため、手元にJar/Warがなかった場合はMaven Repositoryから収集してくれます。 このあたりは便利ですね。

実際に使ってみる

Jarvizは3つのツールから構成されます。このうち、ユーザの意識する必要があるのはjarviz-cliのみでしょう。

ツール名 概要
jarviz-lib byte codeから依存関係を解析し、解析結果をjsonlとして出力する。Java製。
jarviz-graph jsonlファイルに定義された依存関係をwikipedia:力学モデルで可視化する
jarviz-cli Jarvizをユーザとして利用するためのインタフェースとなるコマンドラインツール。

jarviz-cliはサブコマンドを取るCLIツールとなっており、以下のようにgraphサブコマンドを使えば可視化が行われます。

$ ./jarviz graph -f samples/nablarch-filter.json -a samples/nablarch-artifacts.json
(略)
Processed 586 rows and wrote to disk "/Users/kiririmode/src/github.com/ExpediaGroup/jarviz/jarviz-cli/jarviz-results-20220424-114612"
Dependency analysis data were saved to: jarviz-results-20220424-114612/jarviz-dependency-data.jsonl
Dependency graph was saved to: jarviz-results-20220424-114612/jarviz-dependency-data.html

引数を2つ渡していますが、-fで与えるのがフィルタ、-aで渡すのが解析対象のアーティファクトを定義するJSONファイルです。

フィルタ

フィルタで定義されるのは、「依存関係として解析する対象の定義」です。 例えば以下ではnablarch.testパッケージからの依存関係を解析対象とする一方、 nablarch.testからnablarch.testパッケージ内への自己参照的な依存関係は含めません。

このフィルタファイルにより、具体的にどのような依存関係を可視化するのかを指定します。

{
  "include": {
    "sourcePackage": "^(nablarch\\.test)$"
  },
  "exclude": {
    "targetPackage": "^(nablarch\\.test).*$"
  }
}

アーティファクト

アーティファクトは、その名前の通り具体的に解析するアーティファクトを定義します。 例えば以下では、nablarch-corenablarch-testingを依存関係として定義しています。

{
  "appSetName": "Nablarch",
  "applications": [
    {
      "appName": "nablarch",
      "artifacts": [
        {
          "groupId": "com.nablarch.framework",
          "artifactId": "nablarch-core",
          "version": "1.1.0"
        }
      ]
    }
  ],
  "applications": [
    {
      "appName": "nablarch-testing",
      "artifacts": [
        {
          "groupId": "com.nablarch.framework",
          "artifactId": "nablarch-testing",
          "version": "1.4.0"
        }
      ]
    }
  ]
}

出力結果

JSONLファイルでは、アーティファクトとフィルタによって定義される依存関係が格納されます。 具体的に中身をみると以下のような形になります。

$ head -2 jarviz-dependency-data.jsonl | jq
{
  "appSetName": "Nablarch",
  "applicationName": "nablarch-testing",
  "artifactFileName": "nablarch-testing-1.4.0.jar",
  "artifactId": "nablarch-testing",
  "artifactGroup": "com.nablarch.framework",
  "artifactVersion": "1.4.0",
  "sourceClass": "nablarch.test.AbstractStringMatcher",
  "sourceMethod": "<init>",
  "targetClass": "org.hamcrest.BaseMatcher",
  "targetMethod": "<init>"
}
{
  "appSetName": "Nablarch",
  "applicationName": "nablarch-testing",
  "artifactFileName": "nablarch-testing-1.4.0.jar",
  "artifactId": "nablarch-testing",
  "artifactGroup": "com.nablarch.framework",
  "artifactVersion": "1.4.0",
  "sourceClass": "nablarch.test.AbstractStringMatcher",
  "sourceMethod": "describeTo",
  "targetClass": "org.hamcrest.Description",
  "targetMethod": "appendValue"
}

可視化結果

可視化結果は以下のようなものになります。 2つ貼り付けましたが、前者がアプリケーション単位、後者が依存先をベースとした可視化結果になります。 具体的にどう違うのかはドキュメントにも定義されておらず、ソースを読まない限りわかりそうにありません。

なお、グラフ自体はreact-force-graphを用いて記述されていました。 従って、グラフの生成アルゴリズムはwikipedia:力学モデル (グラフ描画アルゴリズム)のようです。

感想

解析結果としてのJSONLファイルについてはかなり使えそうな印象を持ちました。 JSONLなので解析もしやすいとともに、メソッドの呼び出し関係をここまで楽に解析できるのは非常に気持ちが良いです。 メソッドを修正するときの依存関係の確認にも使えそうですね(もちろん、IDEを使えばコールスタックはほぼ辿れるわけですが…)。

一方で、可視化結果としての応用は効きづらい、というのが正直な印象です。 上記のNablarchの解析例をみてもわかる通り、アプリケーション単位の解析結果はほぼ円状になり、ここから何らかの示唆を導くことは困難です。 また規模が大きいアプリケーションの場合は描画負荷が大きくなり、フィルタを適切に定義しないと可視化結果の確認すら叶いません。

そういうわけで当初の目的は果たせなかったわけですが、JSONLファイルの方はまだ応用が効きそうなので、 自前で可視化していければと考えています。