![]() | |
|
jdeps [options] path ...
The jdeps
command shows the package-level or class-level dependencies of Java class files. The input
class can be a path name to a .class
file, a directory, a JAR file, or it can be a fully qualified
class name to analyze all class files. The options determine the output. By default, the jdeps
command
writes the dependencies to the system output. The command can generate the dependencies in DOT language (the
-dotoutput
option).
Possible options:
-dotoutput dir_name
(or --dot-output dir_name
)
Specifies the destination directory for DOT file output. If this option is specified, then the jdeps
command generates one .dot
file for each analyzed archive named archive-file-name.dot
that
lists the dependencies, and also a summary file named summary.dot
that lists the dependencies among
the archive files.
-s
(or -summary
)
Prints a dependency summary only.
-jdkinternals
(or --jdk-internals
)
Finds class-level dependencies in the JDK internal APIs. By default, this option analyzes all classes
specified in the --class-path
option and input files unless you specified the -include
option. You can’t use this option with the -p
, -e
, and -s
options.
Warning: The JDK internal APIs are inaccessible.
-cp path
(or -classpath path
, or --class-path path
)
Specifies where to find class files.
--module-path module_path
Specifies the module path.
–m module_name
(or --module module_name
)
Specifies the root module for analysis.
--generate-module-info dir
Generates module-info.java
under the specified directory. The specified JAR files
will be analyzed. This option cannot be used with --dot-output
or
--class-path
options. Use the --generate-open-module
option
for open modules.
--generate-open-module dir
Generates module-info.java
for the specified JAR files under the specified directory as
open modules. This option cannot be used with the --dot-output
or
--class-path
options.
Assume we have several JARs as follows:
C:\1Z0-817 │ ├───app │ └───by │ └───iba │ └───app │ App.java │ └───info.logger └───by └───iba └───logging InfoLogger.java
package by.iba.app; import by.iba.logging.InfoLogger; import java.util.logging.Logger; public class App { public static void main(String... args) { InfoLogger.log("Application started ..."); Logger logger = InfoLogger.getLog(); logger.info("Application finished."); } }
package by.iba.logging; import java.util.logging.Logger; public class InfoLogger { private static final Logger LOG = Logger.getLogger(InfoLogger.class.getName()); public static void log(String msg) { LOG.info(msg); } public static Logger getLog() { return LOG; } }
javac info.logger\by\iba\logging\InfoLogger.java jar --create --file info-logger.jar -C info.logger . javac -cp info-logger.jar app\by\iba\app\App.java jar --create --file app.jar -C app .
Now we can check class path dependencies:
C:\1Z0-817>jdeps -cp info-logger.jar -s app.jar app.jar -> info-logger.jar app.jar -> java.base app.jar -> java.logging
C:\1Z0-817>jdeps -s info-logger.jar info-logger.jar -> java.base info-logger.jar -> java.logging
We can automatically generate module definitions for the 2 JARs:
C:\1Z0-817>jdeps --generate-module-info . *.jar writing to .\app\module-info.java writing to .\info.logger\module-info.java
The content of generated module-info.java
files as follows:
module app { requires info.logger; requires java.logging; exports by.iba.app; }
module info.logger { requires transitive java.logging; exports by.iba.logging; }
Re-create JARs as modular ones:
C:\1Z0-817>javac info.logger\module-info.java info.logger\by\iba\logging\InfoLogger.java C:\1Z0-817>jar --create --verbose --file info-logger.jar -C info.logger . C:\1Z0-817>javac -p info-logger.jar app\module-info.java app\by\iba\app\App.java C:\1Z0-817>jar --create --verbose --file app.jar -C app .
Now we can check module dependencies:
C:\1Z0-817>jdeps --module-path app.jar;info-logger.jar -summary --module app app -> info.logger app -> java.base app -> java.logging
C:\1Z0-817>jdeps --module-path app.jar;info-logger.jar -summary --module info.logger info.logger -> java.base info.logger -> java.logging
Also, you can visualize the dependencies for all JARs:
C:\1Z0-817>jdeps --module-path app.jar;info-logger.jar --dot-output . *.jar
The results are several .dot
files in the current directory, we
check the summary.dot
:
digraph "summary" { "app" -> "info.logger"; "app" -> "java.base (java.base)"; "app" -> "java.logging (java.logging)"; "info.logger" -> "java.base (java.base)"; "info.logger" -> "java.logging (java.logging)"; }
You can visualize module dependency graph (e.g. at http://www.webgraphviz.com/
):
Cyclic dependencies
Cyclic dependencies between modules can be recognized by java compiler:
C:\1Z0-817 │ ├───modA │ module-info.java │ └───modB module-info.java
module modA { requires modB; }
module modB { requires modA; }
C:\1Z0-817>javac --module-source-path . modA\module-info.java -d modA .\modB\module-info.java:2: error: cyclic dependence involving modA requires modA; ^ error: cannot access module-info cannot resolve modules modA\module-info.java:1: error: module not found: modB module modA { ^ 3 errors
Imagine we have the following application:
C:\1Z0-817 ├───modA │ │ module-info.java │ │ │ └───pkgA │ ClassA.java │ └───modB │ module-info.java │ └───pkgB ClassB.java
module modA { requires modB; exports pkgA; }
package pkgA; import pkgB.ClassB; public class ClassA { public void methodA1() { new ClassB().methodB2(); } public void methodA2() { } }
module modB { requires modA; exports pkgB; }
package pkgB; import pkgA.ClassA; public class ClassB { public void methodB1() { new ClassA().methodA2(); } public void methodB2() { } }
The solutions can be:
Merge pkgA.ClassA
and pkgB.ClassB
into a single module
Break cycle by using interfaces.
Let's try the second solution:
Add third modC
module and refactor all interfaces from class methods which create dependencies:
C:\1Z0-817\modC │ module-info.java │ └───pkgC InterfaceA.java InterfaceB.java
module modC { exports pkgC; }
package pkgC; public interface InterfaceA { public void methodA1(); public void methodA2(); }
package pkgC; public interface InterfaceB { public void methodB1(); public void methodB2(); }
Now, refactor modA
and modB
, to depend not on each other,
but both depend on modC
.
module modA { requires modC; exports pkgA; }
package pkgA; import pkgC.InterfaceA; import pkgC.InterfaceB; public class ClassA implements InterfaceA { InterfaceB bIntf; public ClassA(InterfaceB bIntf) { this.bIntf = bIntf; } @Override public void methodA1() { bIntf.methodB2(); } @Override public void methodA2() { } }
module modB { requires modC; exports pkgB; }
package pkgB; import pkgC.InterfaceA; import pkgC.InterfaceB; public class ClassB implements InterfaceB { InterfaceA aIntf; public ClassB(InterfaceA aIntf) { this.aIntf = aIntf; } @Override public void methodB1() { aIntf.methodA2(); } @Override public void methodB2() { } }
Now you can compile all three modules:
javac --module-source-path . modC\module-info.java modC\pkgC\InterfaceA.java modC\pkgC\InterfaceB.java -d . javac --module-source-path . modA\module-info.java modA\pkgA\ClassA.java -d . javac --module-source-path . modB\module-info.java modB\pkgB\ClassB.java -d .
Create modular JARs:
jar cvf modA.jar -C modA . jar cvf modB.jar -C modB . jar cvf modC.jar -C modC .
Inspect dependency graph visually:
C:\1Z0-817>jdeps --module-path modA.jar;modB.jar;modC.jar --dot-output . *.jar
![]() ![]() ![]() |