Javaパッケージ間の循環参照を検出するライブラリ depDetect
※GUI付の循環参照検出ツールdepDetectGuiはJavaパッケージ間の循環依存を検出するツールdepDetectGuiの紹介にある。弊社製オープンソースソフトはOpen Sourcesとしてまとめているので参照されたい。
※以下は、検出用ライブラリについての説明であり、これにGUIは付属していない。
Javaパッケージ間の循環依存を検出するで書いたことだが、適当に記述された大規模なJavaプログラムがあり、そのもつれあった循環参照を排除したい。
これをJDKバンドルのjdepsコマンドを呼び出して行う。
- githubリポジトリ:https://github.com/ysugimura/depDetect
- mavenリポジトリ:https://ysugimura.github.io/maven
以下の記述は1.4.3時点のものである。
サンプル出力
このライブラリを利用して、このライブラリ自体のソースコードの解析を行わせ、適当に出力させてみたものが以下である(わざと循環依存を起こすようなコードにしてある)。
不明import
java.io
java.lang
java.lang.invoke
java.nio.charset
java.nio.file
java.util
java.util.concurrent
java.util.function
java.util.regex
java.util.stream
org.hamcrest
org.junit
org.junit.runner
org.junit.runners
木構造
com
cm55
depDetect
impl
AllTest
BinTreeCreator
BinTreeDetail
BinTreeDetailTest
BulkImports
ClsDeps
ClsNodeImpl
CommentRemover
CommentRemoverCommentTest
CommentRemoverTest
DepsBuilder
Import
ImportTest
Imports
JavaNodeImpl
PkgNodeImpl
PkgNodeImplTest
RefsImpl
SrcImportExtractor
SrcImportExtractorTest
SrcTreeCreator
SrcTreeDetail
UnknownsImpl
VarImports
package-info
test
Main
ClsNode
CyclicTest
JavaNode
JavaNodeKind
PkgNode
Refs
Unknowns
VisitOrder
package-info
循環依存発生パッケージ:com.cm55.depDetect
依存先パッケージ:com.cm55.depDetect.impl
原因クラス:com.cm55.depDetect.CyclicTest
原因クラス:com.cm55.depDetect.test.Main
循環依存発生パッケージ:com.cm55.depDetect.impl
依存先パッケージ:com.cm55.depDetect
原因クラス:com.cm55.depDetect.impl.BinTreeCreator
原因クラス:com.cm55.depDetect.impl.BinTreeDetail
原因クラス:com.cm55.depDetect.impl.BinTreeDetailTest
原因クラス:com.cm55.depDetect.impl.ClsNodeImpl
原因クラス:com.cm55.depDetect.impl.DepsBuilder
原因クラス:com.cm55.depDetect.impl.JavaNodeImpl
原因クラス:com.cm55.depDetect.impl.PkgNodeImpl
原因クラス:com.cm55.depDetect.impl.PkgNodeImplTest
原因クラス:com.cm55.depDetect.impl.RefsImpl
原因クラス:com.cm55.depDetect.impl.SrcTreeCreator
原因クラス:com.cm55.depDetect.impl.SrcTreeDetail
原因クラス:com.cm55.depDetect.impl.UnknownsImpl
仕組み
仕組みとしてはこうだ。
一つまたは複数の.classフォルダを指定することにより、それについてjdepsを走行させる。jdepsはそれぞれのクラスの依存を標準出力に出力するので、これをパッケージごとの他パッケージへの依存としてまとめる。
これにより、ある一つのパッケージについて「依存するパッケージ」と「依存されているパッケージ」の集合が得られる。
それらの積集合が「循環依存しているパッケージ」になる。AはBに依存しており、BはAに依存しているという状態だ。
ただし、実際に依存を発生させているのはパッケージ自体ではなく、その中に含まれるクラスであるため、その依存を発生させているクラスが特定できるようなデータ構造になっている。
簡単な使い方
このライブラリ自体のJavaソースを対象として、冒頭の出力を得るサンプルは以下になる。
package com.cm55.depDetect.test;
import java.io.*;
import java.util.*;
import com.cm55.depDetect.*;
import com.cm55.depDetect.impl.*;
public class Main {
public static void main(String[] args) throws IOException {
PkgNode root = BinTreeCreator.create(
null,
Arrays.stream(new String[] {
"C:\\devel\\workspace-neon\\github_depDetect\\bin\\default",
})
);
// 不明importを表示
System.out.println("不明import");
root.getUnknowns(true).stream().forEach(System.out::println);
// 木構造を表示
System.out.println("\n木構造");
System.out.println(root.treeString());
// 循環参照を表示
root.visitPackages(VisitOrder.PRE, pkg -> {
Refs cyclics = pkg.getCyclics(false);
if (cyclics.size() == 0)
return;
System.out.println("\n循環依存発生パッケージ:" + pkg);
cyclics.stream().forEach(cyc-> {
System.out.println(" 依存先パッケージ:" + cyc);
pkg.childClasses(true).forEach(cls-> {
if (cls.getDepsTo().contains(cyc)) {
System.out.println(" 原因クラス:" + cls);
}
});
});
});
}
}