![]() | |
|
Assume we have a utility library packaged as separate info-logger.jar
JAR (Java 8.0, non-modular):
C:\1Z0-817\INFO-LOGGER └───by └───iba └───logging InfoLogger.java
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; } }
C:\1Z0-817\info-logger>javac by\iba\logging\InfoLogger.java
C:\1Z0-817\info-logger>jar cvf info-logger.jar . added manifest adding: by/(in = 0) (out= 0)(stored 0%) adding: by/iba/(in = 0) (out= 0)(stored 0%) adding: by/iba/logging/(in = 0) (out= 0)(stored 0%) adding: by/iba/logging/InfoLogger.class(in = 667) (out= 370)(deflated 44%) adding: by/iba/logging/InfoLogger.java(in = 328) (out= 181)(deflated 44%)
And application (Java 8.0, non-modular) JAR:
C:\1Z0-817\APP └───by └───iba └───app App.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."); } }
Application depends on info-logger.jar
utility JAR:
C:\1Z0-817\app>javac -cp ../info-logger/info-logger.jar by\iba\app\App.java
C:\1Z0-817\app>jar --verbose --create --file app.jar . added manifest adding: by/(in = 0) (out= 0)(stored 0%) adding: by/iba/(in = 0) (out= 0)(stored 0%) adding: by/iba/app/(in = 0) (out= 0)(stored 0%) adding: by/iba/app/App.class(in = 513) (out= 348)(deflated 32%) adding: by/iba/app/App.java(in = 320) (out= 176)(deflated 45%)
We should get similar hierarchy:
C:\1Z0-817 ├───app │ │ app.jar │ │ │ └───by │ └───iba │ └───app │ App.class │ App.java │ └───info-logger │ info-logger.jar │ └───by └───iba └───logging InfoLogger.class InfoLogger.java
Now you can run the application in Java 8.0 style:
C:\1Z0-817>java -classpath ./info-logger/info-logger.jar;./app/app.jar by.iba.app.App Apr 20, 2019 11:56:21 PM by.iba.logging.InfoLogger log INFO: Application started ... Apr 20, 2019 11:56:21 PM by.iba.app.App main INFO: Application finished.
The structure of application looks as follows:
Migrating from the top down
We put all JARs (application and utility) to module path rather than class path and make application JAR modular.
Check dependencies for application to create proper module descriptor:
C:\1Z0-817>jdeps --module-path info-logger/info-logger.jar -s app/app.jar app.jar -> info.logger app.jar -> java.base app.jar -> java.logging info.logger -> java.base info.logger -> java.logging
Application depends on 3 modules: info.logger
(automatic module name for info-logger.jar
), java.base
(implicitly required by
any module), and java.logging
.
Assume we have no source for info-logger.jar
and use it as automatic module:
Real module, but without module descriptor.
We do not change someone else's JAR file
Module name derived from JAR file name
Exports all its packages
Requires all other modules
Based on this we create a module definition for app
application module:
module app { requires info.logger; requires java.logging; }
C:\1Z0-817\APP │ module-info.java │ └───by └───iba └───app App.java
Recompile the application code:
javac -p ../info-logger/info-logger.jar module-info.java by\iba\app\App.java
NOTE: -p
is synonym for --module-path
Recreate the application modular JAR file:
C:\1Z0-817\app>jar --verbose --create --file app.jar . added manifest added module-info: module-info.class adding: by/(in = 0) (out= 0)(stored 0%) adding: by/iba/(in = 0) (out= 0)(stored 0%) adding: by/iba/app/(in = 0) (out= 0)(stored 0%) adding: by/iba/app/App.class(in = 513) (out= 348)(deflated 32%) adding: by/iba/app/App.java(in = 320) (out= 176)(deflated 45%) adding: module-info.java(in = 70) (out= 53)(deflated 24%)
Run the application:
C:\1Z0-817>java --module-path ./info-logger/info-logger.jar;./app/app.jar -m app/by.iba.app.App Apr 21, 2019 12:33:03 AM by.iba.logging.InfoLogger log INFO: Application started ... Apr 21, 2019 12:33:03 AM by.iba.app.App main INFO: Application finished.
NOTE: -m
is synonym for --module
Migrating from the bottom up
We make all library JARs modular and put on the module path, keep application JAR non-modular and put on the class path (it will become part of the unnamed module).
Revert app.jar
back to non-modular (it will be part of unnamed module):
C:\1Z0-817\app>jar -tvf app.jar 0 Sun Apr 21 10:46:24 AST 2019 META-INF/ 94 Sun Apr 21 10:46:24 AST 2019 META-INF/MANIFEST.MF 0 Fri Apr 19 22:29:28 AST 2019 by/ 0 Fri Apr 19 22:29:34 AST 2019 by/iba/ 0 Sat Apr 20 23:40:42 AST 2019 by/iba/app/ 513 Sun Apr 21 00:28:22 AST 2019 by/iba/app/App.class 320 Sat Apr 20 23:40:32 AST 2019 by/iba/app/App.java
The unnamed module:
Reads all other modules
Exports all its packages
Cannot have any dependencies declared on it
Cannot be accessed by a named module (the one with module-info.class
)
Check dependencies for library utility JAR to create a module definition (module-info.java
):
C:\1Z0-817>jdeps -s info-logger/info-logger.jar info-logger.jar -> java.base info-logger.jar -> java.logging
Since java.base
always implicitly required by any module, we may think that we require only java.logging
module
and come up with this definition:
// WRONG module info.logger { requires java.logging; }
But this module definition is wrong, because the library must export packages to be used by application (or by unnamed)
module. We need to use jdeps
to generate accurate module-info.java
for us:
C:\1Z0-817\info-logger>jdeps --generate-module-info . info-logger.jar writing to .\info.logger\module-info.java
The generated module-info.java
is writen into info.logger\module-info.java
, move it 1 level higher.
The correct definition will look as follows (NOTE: requires transitive
also was recognized and added by jdeps
):
// CORRECT module info.logger { requires transitive java.logging; exports by.iba.logging; }
![]() | |
NOTE: module name may not contain dash character, it get replaced with period character automatically by |
Re-compile the utility modular JAR sources:
C:\1Z0-817\info-logger>javac module-info.java by\iba\logging\InfoLogger.java
C:\1Z0-817\INFO-LOGGER │ module-info.class │ module-info.java │ └───by └───iba └───logging InfoLogger.class InfoLogger.java
Re-create the modular JAR:
C:\1Z0-817\info-logger>jar --verbose --create --file info-logger.jar . added manifest added module-info: module-info.class adding: by/(in = 0) (out= 0)(stored 0%) adding: by/iba/(in = 0) (out= 0)(stored 0%) adding: by/iba/logging/(in = 0) (out= 0)(stored 0%) adding: by/iba/logging/InfoLogger.class(in = 667) (out= 370)(deflated 44%) adding: by/iba/logging/InfoLogger.java(in = 328) (out= 181)(deflated 44%) adding: module-info.java(in = 97) (out= 78)(deflated 19%)
Now we can run the modularized application:
C:\1Z0-817>java --add-modules info.logger --module-path info-logger/info-logger.jar --class-path app/app.jar by.iba.app.App Apr 21, 2019 11:45:06 AM by.iba.logging.InfoLogger log INFO: Application started ... Apr 21, 2019 11:45:06 AM by.iba.app.App main INFO: Application finished.
![]() | |
When the compiler compiles code in the unnamed module, or the Java launcher is invoked and the main class of the application is loaded from the class path into the unnamed module of the application class loader, then the default set of root modules for the unnamed module is computed as follows:
Otherwise, the default set of root modules depends upon the phase:
It is occasionally necessary to add modules to the default root set in order to ensure that specific platform, library, or
service-provider modules will be present in the module graph. In any phase the option |
So, we had to use --add-modules info.logger
option to tell the Java runtime to include the module by name to the default root set. There are also
predefined constants: ALL-MODULE-PATH
, ALL-DEFAULT
, and ALL-SYSTEM
.
The --module-path
option tells the Java runtime the location of our modules.
![]() ![]() ![]() |