JDK8からjdeps(http://docs.oracle.com/javase/8/docs/technotes/tools/unix/jdeps.html)というJavaのクラス間の依存性を調べるツールが追加されました。
Java Day Tokyo2014ではこのツールを使ってJava9のProject Jigsawのための作業をしていると言っていたような気がしましたが…、そんなことより、このツールすごくSIer向けだと思いました。
使い方
使い方は簡単。あらかじめ依存性を調べたいソースをコンパイルしておいて
jdeps コンパイルしたクラスのディレクトリ または jdeps JARファイル
とするだけ。(依存クラス・JARがJDK以外にいる場合は別途クラスパス指定可能)
そうすると
$> jdeps clojure clojure -> /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar clojure (clojure) -> java.awt -> java.beans -> java.io -> java.lang -> java.lang.annotation -> java.lang.reflect -> java.math -> java.net -> java.sql -> java.text -> java.util -> java.util.concurrent -> java.util.regex -> javax.swing -> javax.swing.table -> javax.xml.parsers -> org.xml.sax -> sun.misc JDK internal API (rt.jar) clojure.asm (clojure) -> java.io -> java.lang -> java.lang.reflect clojure.asm.commons (clojure) -> java.io -> java.lang -> java.lang.reflect -> java.security -> java.util clojure.core (clojure) -> java.lang -> java.util -> java.util.concurrent -> java.util.concurrent.atomic (以下略)
(例としてClojure - https://github.com/clojure/clojure を使いました)
-v
で、クラスごと出すことができます。
$> jdeps -v clojure clojure -> /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar clojure.asm.AnnotationVisitor -> clojure.asm.Opcodes clojure clojure.asm.AnnotationVisitor -> java.lang.IllegalArgumentException clojure.asm.AnnotationVisitor -> java.lang.Object clojure.asm.AnnotationVisitor -> java.lang.String clojure.asm.AnnotationWriter -> clojure.asm.AnnotationVisitor clojure clojure.asm.AnnotationWriter -> clojure.asm.ByteVector clojure clojure.asm.AnnotationWriter -> clojure.asm.ClassWriter clojure clojure.asm.AnnotationWriter -> clojure.asm.Item clojure clojure.asm.AnnotationWriter -> clojure.asm.Opcodes clojure clojure.asm.AnnotationWriter -> clojure.asm.Type clojure clojure.asm.AnnotationWriter -> java.lang.Boolean clojure.asm.AnnotationWriter -> java.lang.Byte clojure.asm.AnnotationWriter -> java.lang.Character clojure.asm.AnnotationWriter -> java.lang.Object clojure.asm.AnnotationWriter -> java.lang.Short clojure.asm.AnnotationWriter -> java.lang.String clojure.asm.Attribute -> clojure.asm.ByteVector clojure (以下略)
-p
で指定のパッケージだけ出すということもできます。
$> jdeps -v -p clojure.core clojure clojure.core$fn__6136$__GT_VecNode__6138 -> clojure.core.VecNode clojure clojure.core$fn__6145$__GT_ArrayChunk__6147 -> clojure.core.ArrayChunk clojure clojure.core$fn__6150$__GT_VecSeq__6158 -> clojure.core.VecSeq clojure clojure.core$fn__6165$__GT_Vec__6193 -> clojure.core.Vec clojure clojure.core$reify__6203 -> clojure.core.ArrayManager clojure clojure.core$reify__6206 -> clojure.core.ArrayManager clojure clojure.core$reify__6209 -> clojure.core.ArrayManager clojure clojure.core$reify__6212 -> clojure.core.ArrayManager clojure clojure.core$reify__6215 -> clojure.core.ArrayManager clojure clojure.core$reify__6218 -> clojure.core.ArrayManager clojure clojure.core$reify__6221 -> clojure.core.ArrayManager clojure clojure.core$reify__6224 -> clojure.core.ArrayManager clojure clojure.core$vector_of -> clojure.core.ArrayManager clojure clojure.core$vector_of -> clojure.core.Vec clojure
-e
で正規表現も使えます。
$> jdeps -v -e '.*\$.*' clojure clojure -> /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar clojure.asm.commons.SerialVersionUIDAdder -> clojure.asm.commons.SerialVersionUIDAdder$Item clojure clojure.asm.commons.SerialVersionUIDAdder$Item -> clojure.asm.commons.SerialVersionUIDAdder$Item clojure clojure.core$_ -> clojure.core$_ clojure clojure.core$_DOT__DOT_ -> clojure.core$_DOT__DOT_ clojure clojure.core$_EQ_ -> clojure.core$_EQ_ clojure clojure.core$_EQ__EQ_ -> clojure.core$_EQ__EQ_ clojure clojure.core$_EQ__EQ___inliner -> clojure.core$_EQ__EQ___inliner clojure clojure.core$_EQ___inliner -> clojure.core$_EQ___inliner clojure clojure.core$_GT_ -> clojure.core$_GT_ clojure clojure.core$_GT_0_QMARK_ -> clojure.core$_GT_0_QMARK_ clojure clojure.core$_GT_1_QMARK_ -> clojure.core$_GT_1_QMARK_ clojure clojure.core$_GT__EQ_ -> clojure.core$_GT__EQ_ clojure clojure.core$_GT__EQ___inliner -> clojure.core$_GT__EQ___inliner clojure clojure.core$_GT___inliner -> clojure.core$_GT___inliner clojure clojure.core$_LT_ -> clojure.core$_LT_ clojure clojure.core$_LT__EQ_ -> clojure.core$_LT__EQ_ clojure clojure.core$_LT__EQ___inliner -> clojure.core$_LT__EQ___inliner clojure
-profile
で、JREをどれだけ切り詰められるか、プロファイルをしりたい人にも親切。
jdeps -profile clojure clojure -> /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar (Full JRE) clojure (clojure) -> java.awt Full JRE -> java.beans Full JRE -> java.io compact1 -> java.lang compact1 -> java.lang.annotation compact1 -> java.lang.reflect compact1 -> java.math compact1 -> java.net compact1 -> java.sql compact2 -> java.text compact1 -> java.util compact1 -> java.util.concurrent compact1 -> java.util.regex compact1 -> javax.swing Full JRE -> javax.swing.table Full JRE -> javax.xml.parsers compact2 -> org.xml.sax compact2 -> sun.misc JDK internal API (rt.jar)
-dotoutput 出力フォルダ
のオプションで、Graphviz形式で依存性の有向グラフも出すことができます。
$> jdeps -dotoutput dot clojure $> ls dot clojure.dot summary.dot $> dot -Tgif dot/clojure.dot > clojure.gif
SIerやらとの親和性
「○○というクラスを修正したいけど、どこに影響が及ぶか知りたい。」というケースはよくあります。
まあEclipseでCtrl-Shift-G(シンボルの参照の検索)を連打してもいいのですが、やっぱめんどいですね。
SonarQubeとかでもクラスの依存性の解析は出たりしますが、ちょっとめんどいですね。
そういう人はJenkinsでコンパイルのついでにjdepsの出力結果を常に出しておくと便利です。私はそうしています。
複数プロジェクトがくんずほぐれつしている場合でも引数にクラスパスをちゃんと指定しておけばちゃんと複数プロジェクト間の依存性を見られます。
変なモジュールの絡み方してるやつも一発で見つけられますね。
みなさんもぜひ活用しましょう。
まとめ
jdeps
コマンドを使えばクラス(パッケージ)間の依存性をチェックできる。- 業務開発で出がちなモジュール間の依存性チェックに使える。