JDK8のjdepsは普通の業務開発でも便利

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

f:id:knjname:20140603010217g:plain

SIerやらとの親和性

「○○というクラスを修正したいけど、どこに影響が及ぶか知りたい。」というケースはよくあります。

まあEclipseでCtrl-Shift-G(シンボルの参照の検索)を連打してもいいのですが、やっぱめんどいですね。

SonarQubeとかでもクラスの依存性の解析は出たりしますが、ちょっとめんどいですね。

そういう人はJenkinsでコンパイルのついでにjdepsの出力結果を常に出しておくと便利です。私はそうしています。

複数プロジェクトがくんずほぐれつしている場合でも引数にクラスパスをちゃんと指定しておけばちゃんと複数プロジェクト間の依存性を見られます。

変なモジュールの絡み方してるやつも一発で見つけられますね。

みなさんもぜひ活用しましょう。

まとめ

  • jdepsコマンドを使えばクラス(パッケージ)間の依存性をチェックできる。
  • 業務開発で出がちなモジュール間の依存性チェックに使える。