Jigsaw study notes

A note from studying Java 9 Project Jigsaw.

environment

OS Windows 10

Java

>java --version
java 9
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)

Hello World

Folder structure

|-main/
| |-class/
| `-src/
|   |-module-info.java
|   `-sample/main/
|     `-Main.java
|
`-sub/
  |-class/
  `-src/
    |-module-info.java
    `-sample/sub/
      |-api/
      | `-Foo.java
      |-internal/
      | `-FooImpl.java
      |
      `-FooFactory.java

Source code

sub/src/sample/sub/api/Foo.java


package sample.sub.api;

public interface Foo {
    void hello();
}

sub/src/sample/sub/internal/FooImpl.java


package sample.sub.internal;

import sample.sub.api.Foo;

public class FooImpl implements Foo {
    @Override
    public void hello() {
        System.out.println("FooImpl.hello()");
    }
}

sub/src/sample/sub/FooFactory.java


package sample.sub;

import sample.sub.api.Foo;
import sample.sub.internal.FooImpl;

public class FooFactory {
    public static Foo newInstance() {
        return new FooImpl();
    }
}

sub/src/module-info.java


module sample_module.sub {
    exports sample.sub;
    exports sample.sub.api;
}

main/src/sample/main/Main.java


package sample.main;

import sample.sub.FooFactory;
import sample.sub.api.Foo;

public class Main {
    public static void main(String... args) {
        Foo foo = FooFactory.newInstance();
        foo.hello();
    }
}

main/src/module-info.java


module sample_module.main {
    requires sample_module.sub;
}

compile

#Compile the sub module
> javac -d sub\class sub\src\module-info.java sub\src\sample\sub\FooFactory.java sub\src\sample\sub\api\Foo.java sub\src\sample\sub\internal\FooImpl.java

#Compile the main module
> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java

Execute

> java -p main\class;sub\class -m sample_module.main/sample.main.Main
FooImpl.hello()

Description

Dependent module declaration

sub/src/module-info.java


module sample_module.sub {
    exports sample.sub;
    exports sample.sub.api;
}

--The module declaration is written in a special file called module-info.java placed in the root package. --Name is fixed to module-info.java --Module declaration is described in the format module <module name> {<module definition>} --Module names can be separated by dots ., just like package names. --Usually, it seems that the name is the same as the package name, but in that case it is difficult to distinguish between the package name and the module name in the sample implementation, so here I dare to name sample_module.sub different from the package. Attached --You can write a definition for this module in {} --ʻExportsspecifies packages to be exposed to the outside --Here, two packages,sample.sub and sample.sub.api, are open to the outside. --sample.sub.internal` is intended as an internal API, so I try not to publish it.

Module declaration on the referencing side

main/src/module-info.java


module sample_module.main {
    requires sample_module.sub;
}

--The side that refers to other modules also describes the module declaration using module-info.java as well as the side that is referenced. --requries defines the modules this module depends on --Here, it is defined to refer to the sample_module.sub module mentioned earlier. --I saw the explanation that you can't start the package (sample.main) with the main class without ʻexports, but when I actually tried it, it worked even if I didn't ʻexports. --Did the specifications change on the way?

Module specification at compile time

#Compile sub module
> javac -d sub\class sub\src\module-info.java sub\src\sample\sub\FooFactory.java sub\src\sample\sub\api\Foo.java sub\src\sample\sub\internal\FooImpl.java

#Compile main module
> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java

--Compile still uses the javac command --If there is no dependent module, you can compile with the conventional specifications (compilation of sub module) --Don't forget to include module-info.java in the source to be compiled! --If there are dependent modules, specify the path of the folder where the dependent modules are located with the -p (or --module-path) option. --In older articles, this option was introduced in -mp, but it seems that it has changed.

Module specification and main class specification at startup

> java -p main\class;sub\class -m sample_module.main/sample.main.Main

--Also at startup, specify the location of the module to use with the -p (--module-path) option --If there are multiple paths, specify them by concatenating them with a semicolon ; for Windows and a colon : for Linux. --Also, the specification of the main class has been changed to specify with the -m (--module) option (when using a module). --The specification of the main class name has also changed, and specify <module name> / <main class name> --Here, sample_module.main / sample.main.Main is used to specify the sample.main.Main class of the sample_module.main module.

Behavior when referencing packages that have not been exported

Main.java


package sample.main;

import sample.sub.FooFactory;
import sample.sub.api.Foo;
import sample.sub.internal.FooImpl;

public class Main {
    public static void main(String... args) {
        FooImpl foo = (FooImpl)FooFactory.newInstance();
        foo.hello();
    }
}

Compile error


> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java
main\src\sample\main\Main.java:5:error:Package sample.sub.internal cannot be displayed
import sample.sub.internal.FooImpl;
                 ^
  (Package sample.sub.internal is the module sample_module.Declared in sub but not exported)
1 error

Like this, you can't refer to packages that aren't ʻexports`.

Package and use in jar

Try using the Hello World program by packaging both main and sub in a jar.

Same up to compiling with javac.

Package in jar

#Create jar for sub module
> jar -c -f sub\jar\sub-module.jar -C sub\class .

#Create jar for main module
> jar -c -f main\jar\main-module.jar -C main\class .

--Use the jar command as before to create the module jar file -- -c is a command that means create a jar (short for --create) --Specify the output destination with -f <jar output destination path> -- -C <directory to package> . to package the specified directory into a jar

Refer to the module information of jar

#Output sub module information
> jar -d -f sub\jar\sub-module.jar
sample_module.sub jar:file:///.../sub/jar/sub-module.jar/!module-info.class
exports sample.sub
exports sample.sub.api
requires java.base mandated
contains sample.sub.internal

#Output main module information
> jar -d -f main\jar\main-module.jar
sample_module.main jar:file:///.../main/jar/main-module.jar/!module-info.class
requires java.base mandated
requires sample_module.sub
contains sample.main

-- -d (--describe-module) can output the module information of jar. --ʻExports is a public package --requires is a dependent module -- java.base is a module that includes the java.lang package, and it always comes with it even if you do not specify it. --contains` is probably a private package

Execute

> java -p sub\jar;main\jar -m sample_module.main/sample.main.Main
FooImpl.hello()

--If you specify the folder with the module ( jar) with -p, it seems that the jar there will be read. --You may directly enter the path to the jar file

Module type

There are three main types of modules.

  1. named module
  2. automatic module
  3. unnamed module

named module Module with definition information (module-info.class) read by module path (--module-path). When we talk about modules, we often refer to them.

automatic module A jar with no definition information read in the module path.

All packages are treated as ʻexports`. Also, the module name is automatically completed from the jar file name.

This is the case if you specify a jar file created before Java 9 with --module-path.

I haven't found a clear definition, but it seems that it should be a jar file because the module name is completed from the file name. Javadoc also has the following description, so I feel that it needs to be a jar at a minimum.

A JAR file that does not have a module-info.class in its top-level directory defines an automatic module (Translation) Jar files that do not have module-info.class at the top level define an automatic module

http://download.java.net/java/jigsaw/docs/api/java/lang/module/ModuleFinder.html#of-java.nio.file.Path...-

unnamed module Modules loaded with the classpath (--class-path). All packages are treated as ʻexports`.

Combine each module

Try various combinations of each module (named module, automatic module, unnamed module).

If you tabulate the results first,

jigsaw.jpg

It looks like this.

Implementation (common)

sub/src/sample/sub/Sub.java


package sample.sub;

public class Sub {
    public void method() {
        System.out.println("Sub.method()");
    }
}

main/src/sample/main/Main.java


package sample.main;

import sample.sub.Sub;

public class Main {
    public static void main(String... args) {
        Sub sub = new Sub();
        sub.method();
    }
}

named module -> named module Hello World is this, so I will omit it.

named module -> automatic module

Folder structure


|-main/
| |-class/
| `-src/
|   |-module-info.java
|   `-sample/main/
|     `-Main.java
|
`-sub/
  |-class/
  `-src/
    `-sample/sub/
      `-Sub.java

Compile sub module


#compile
> javac -d sub\class sub\src\sample\sub\Sub.java

#jar creation
> jar -c -f sub\jar\no_module-sub.jar -C sub\class .

main\src\module-info.java


module sample_module.main {
    requires no.module.sub;
}

--The name of the sub module specified in requires specifies the name that is completed from the jar file name. --In the case of no_module-sub.jar, the module name will be no.module.sub --Detailed conversion rules will be described later

Compile and launch the main module


#Compile main module
> javac -p sub\jar\no_module-sub.jar -d main\class main\src\module-info.java main\src\sample\main\Main.java

#Start-up
> java -p sub\jar\no_module-sub.jar;main\class -m sample_module.main/sample.main.Main
Sub.method()

--Set the sub module to -p (--module-path) and start it

Automatic module name determination logic for automatic module

  1. Manifest file ʻAutomatic-Module-Name` attribute
  2. Automatically determined from the file name if not specified in the manifest file --Excluding the trailing .jar --Extract only before the regular expression -(\ d + (\. | $)) --The part after this regular expression is treated as a version string --xxx-1.1.2.jar extracts xxx as the module name --Replace characters other than alphanumeric characters ([^ A-Za-z0-9]) with dots (.) --If the dot (.) is repeated, replace it with one dot.

named module -> unnamed module I can't seem to do it.

main/src/module-info.java


module sample_module.main {
    // requires no.module.sub;
}
> javac -cp sub\jar\no_module-sub.jar -d main\class main\src\module-info.java main\src\sample\main\Main.java
main\src\sample\main\Main.java:3:error:Package sample.sub does not exist
import sample.sub.Sub;
                 ^
main\src\sample\main\Main.java:7:error:Can't find symbol
        Sub sub = new Sub();
        ^
symbol:Class Sub
place:Class Main
main\src\sample\main\Main.java:7:error:Can't find symbol
        Sub sub = new Sub();
                      ^
symbol:Class Sub
place:Class Main
3 errors

Even if I set jar in the classpath (-cp), the class cannot be read.

automatic module -> named module

Folder structure


|-main/
| |-class/
| `-src/
|   `-sample/main/
|     `-Main.java
|
`-sub/
  |-class/
  `-src/
    |-module-info.java
    `-sample/sub/
      `-Sub.java

sub/src/module-info.java


module sample_module.sub {
    exports sample.sub;
}

Compile sub module


#compile
> javac -d sub\class sub\src\module-info.java sub\src\sample\sub\Sub.java

#jar creation
> jar -c -f sub\jar\module-sub.jar -C sub\class .

Compile main module


#compile
> javac -d main\class --add-modules sample_module.sub -p sub\jar\module-sub.jar main\src\sample\main\Main.java

#jar creation
> jar -c -f main\jar\no_module-main.jar -C main\class .

Run


> java -p sub\jar\module-sub.jar;main\jar\no_module-main.jar --add-modules sample_module.sub -m no.module.main/sample.main.Main
Sub.method()

--The main module works as an automatic module because it is specified in -p (--module-path) without module-info.java. --But because there is no module-info.java, sample_module.sub is not requires and is in an unloaded state, maybe (expressed as not present on the module graph). Should I do it?) --You can use --add-modules to add any module to what is called a root module. --I don't know exactly what the root module is, but I think it's probably adding a module as the starting point for the dependency graph. --This will load the sample_module.sub module and allow you to use the classes there. --It seems that the automatic module is treated as if all other modules are requires, so it seems that the module added as the root module can be referenced from the automatic module. --In other words, it seems that you can refer to the sub module added to the root module with --add-modules from the main module (automatic module).

This is an imagination looking at Togetter of the Gods, so it may or may not be different.

automatic module -> automatic module

Folder structure


|-main/
| |-class/
| `-src/
|   `-sample/main/
|     `-Main.java
|
`-sub/
  |-class/
  `-src/
    `-sample/sub/
      `-Sub.java

Compile sub module


#compile
> javac -d sub\class sub\src\sample\sub\Sub.java

#jar creation
> jar -c -f sub\jar\no_module-sub.jar -C sub\class .

Compile main module


#compile
> javac -d main\class -p sub\jar\no_module-sub.jar --add-modules no.module.sub main\src\sample\main\Main.java

# jar 
> jar -c -f main\jar\no_module-main.jar -C main\class .

Start-up


> java -p sub\jar\no_module-sub.jar;main\jar\no_module-main.jar --add-modules no.module.sub -m no.module.main/sample.main.Main
Sub.method()

--This can also be executed by specifying --add-modules. --Since the main module is loaded as the main class, does it need to be specified in --add-modules?

automatic module -> unnamed module

Compile sub module


#compile
> javac -d sub\class sub\src\sample\sub\Sub.java

#jar creation
> jar -c -f sub\jar\no_module-sub.jar -C sub\class .

Compile main module


#compile
> javac -d main\class -cp sub\jar\no_module-sub.jar main\src\sample\main\Main.java

# jar 
> jar -c -f main\jar\no_module-main.jar -C main\class .

Run


> java -p main\jar\no_module-main.jar -cp sub\jar\no_module-sub.jar -m no.module.main/sample.main.Main
Sub.method()

--Can be treated as an unnamed module by setting the non-module jar in the classpath with -cp

unnamed module -> named module

Folder structure


|-main/
| |-class/
| `-src/
|   `-sample/main/
|     `-Main.java
|
`-sub/
  |-class/
  `-src/
    |-module-info.java
    `-sample/sub/
      `-Sub.java

sub/src/module-info.java


module sample_module.sub {
    exports sample.sub;
}

Compile sub module


#compile
> javac -d sub\class sub\src\module-info.java sub\src\sample\sub\Sub.java

# jar 
> jar -c -f sub\jar\module-sub.jar -C sub\class .

Compile main module


#compile
> javac -p sub\jar\module-sub.jar --add-modules sample_module.sub -d main\class main\src\sample\main\Main.java

#jar creation
> jar -c -f main\jar\no_module-main.jar -C main\class .

Run


> java -p sub\jar\module-sub.jar -cp main\jar\no_module-main.jar --add-modules sample_module.sub sample.main.Main
Sub.method()

--sub If you specify the module name with --add-modules, it works. --Since the main side is an unnamed module, the main class specification is the old way without -m.

unnamed module -> automatic module

Folder structure


|-main/
| |-class/
| `-src/
|   `-sample/main/
|     `-Main.java
|
`-sub/
  |-class/
  `-src/
    `-sample/sub/
      `-Sub.java

Compile sub module


#compile
> javac -d sub\class sub\src\sample\sub\Sub.java

#jar creation
> jar -c -f sub\jar\no_module-sub.jar -C sub\class .

Compile main module


#compile
> javac -p sub\jar\no_module-sub.jar -d main\class --add-modules no.module.sub main\src\sample\main\Main.java

#jar creation
> jar -c -f main\jar\no_module-main.jar -C main\class .

Run


> java -p sub\jar\no_module-sub.jar -cp main\jar\no_module-main.jar --add-modules no.module.sub sample.main.Main
Sub.method()

--It works if you specify the module name that is completed from the file name with --add-modules.

unnamed module -> unnamed module

Compile sub module


#compile
> javac -d sub\class sub\src\sample\sub\Sub.java

#jar creation
> jar -c -f sub\jar\no_module-sub.jar -C sub\class .

Compile main module


#compile
> javac -d main\class -cp sub\jar\no_module-sub.jar main\src\sample\main\Main.java

#jar creation
> jar -c -f main\jar\no_module-main.jar -C main\class .

Run


> java -cp sub\jar\no_module-sub.jar;main\jar\no_module-main.jar sample.main.Main
Sub.method()

--This is basically a module-free method, which is the same as compiling and running normally in Java 8 and below.

requires modifier

One of the following two modifiers can be set in requires.

transitive

Module dependencies


A -> B -> C

Suppose a module B depends on C. It is this transitive qualifier that allows C to automatically make requires when another module ʻA loads Bwithrequires`.

First, if there is no transitive

Folder structure


|-A/
| `-src/
|   |-module-info.java
|   `-a/
|     `-A.java
|-B/
| `-src/
|   |-module-info.java
|   `-b/
|     `-B.java
`-C/
  `-src/
    |-module-info.java
    `-c/
      `-C.java

** C module **

C.java


package c;

public class C {}

C/src/module-info.java


module module_c {
    exports c;
}

** B module **

B.java


package b;

import c.C;

public class B {}

B/src/module-info.java


module module_b {
    requires module_c;
    exports b;
}

** A module **

A.java


package a;

import b.B;
import c.C;

public class A {}

A/src/module-info.java


module module_a {
    requires module_b;
    // requires module_c;Try without requiring C module
    exports a;
}

compile

#Compiling C module
> javac -d c\class C\src\module-info.java C\src\c\C.java

#Compiling B module
> javac -p c\class -d b\class B\src\module-info.java B\src\b\B.java

#Compiling A module
> javac -p c\class;b\class -d a\class A\src\module-info.java A\src\a\A.java
A\src\a\A.java:4:error:Package c cannot be displayed
import c.C;
       ^
  (Package c is module module_Declared in c, but module module_Not loaded in a)
1 error

The A module does not explicitly requires the C module, so the package c cannot be referenced.

Use transitive

B\src\module-info.java


module module_b {
    requires transitive module_c;
    exports b;
}

--Added transitive to C module which was required in module-info.java of B module

#Compiling B module
> javac -p c\class -d b\class B\src\module-info.java B\src\b\B.java

#Compiling A module
> javac -p c\class;b\class -d a\class A\src\module-info.java A\src\a\A.java

--This time I was able to compile without error

static Use static for modules that are" required at compile time but optional at runtime ".

If there is no static

Folder structure


|-main/
| `-src/
|   |-module-info.java
|   `-sample/main/
|     |-SubFactory.java
|     `-Main.java
`-sub/
  `-src/
    |-module-info.java
    `-sample/sub/
      `-Sub.java

Sub.java


package sample.sub;

public class Sub {}

sub\src\module-info.java


module sample_module.sub {
    exports sample.sub;
}

SubFactory.java


package sample.main;

import sample.sub.Sub;

public class SubFactory {
    public static Sub newSub() {
        return new Sub();
    }
}

Main.java


package sample.main;

public class Main {
    public static void main(String... args) {
        System.out.println("Hello World!!");
    }
}

main/src/module-info.java


module sample_module.main {
    requires sample_module.sub;
}

--The main module has a class called SubFactory that references the class of the sub module --For this reason, the sub module is required at compile time. --However, since the Main class does not use SubFactory, the sub module is not needed at startup.

Try compiling.

compile


#Compile sub module
> javac -d sub\class sub\src\module-info.java sub\src\sample\sub\Sub.java

#Compile main module
> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java main\src\sample\main\SubFactory.java

The compilation passes. Then try running it.

> java -p main\class -m sample_module.main/sample.main.Main
Error occurred during initialization of boot layer
java.lang.module.FindException: Module sample_module.sub not found, required by sample_module.main

An error occurred because the sub module could not be found.

When static is added

main\src\module-info.java


module sample_module.main {
    requires static sample_module.sub;
}

Compile and execute in the same way with static.

#Compile main module
> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java main\src\sample\main\SubFactory.java

#Run
> java -p main\class -m sample_module.main/sample.main.Main
Hello World!!

I was able to execute it.

open It cannot be referenced at compile time, but it can be controlled so that it can be referenced at runtime. This will be used when you are referencing the code using reflection.

There are two types, ʻopens, which is set for each package, and ʻopen module, which is set for the entire module.

opens

Folder structure


|-main/
| `-src/
|   |-module-info.java
|   `-sample/main/
|     `-Main.java
`-sub/
  `-src/
    |-module-info.java
    `-sample/sub/
      `-Sub.java

Sub.java


package sample.sub;

public class Sub {
    public void sub() {
        System.out.println("Sub.sub()");
    }
}

sub\src\module-info.java


module sample_module.sub {
    //First, create it without publishing it at all
}

Main.java


package sample.main;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Main {
    public static void main(String... args) throws Exception {
        Class<?> subClass = Class.forName("sample.sub.Sub");
        Constructor<?> constructor = subClass.getConstructor();
        Object sub = constructor.newInstance();

        Method method = subClass.getMethod("sub");
        method.invoke(sub);
    }
}

The sample.sub package is not directly referenced, and theSub.sub ()method is executed using reflection.

main/src/module-info.java


module sample_module.main {
    requires sample_module.sub;
}

Try compiling.

#Compile sub module
> javac -d sub\class sub\src\module-info.java sub\src\sample\sub\Sub.java

#Compile main module
> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java

sample.sub is not statically referenced, so it compiles. But when you do this.

> java -p sub\class;main\class -m sample_module.main/sample.main.Main
Exception in thread "main" java.lang.IllegalAccessException: class sample.main.Main (in module sample_module.main) cannot access class sample.sub.Sub (in module sample_module.sub) because module sample_module.sub does not export sample.sub to module sample_module.main
        at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
        at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:589)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:479)
        at sample_module.main/sample.main.Main.main(Main.java:10)

ʻIllegalAccessException` is thrown.

Publish sample.sub with ʻopens` and try again.

sub\src\module-info.java


module sample_module.sub {
    opens sample.sub;
}

Compile and run


#Compile sub module
> javac -d sub\class sub\src\module-info.java sub\src\sample\sub\Sub.java

#Compile main module
> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java

#Run
> java -p sub\class;main\class -m sample_module.main/sample.main.Main
Sub.sub()

This time it worked fine.

By the way, if you refer to the sample.sub package in Main.java in this state (ʻopens sample.sub; `), it will be as follows.

Main.java


package sample.main;

import sample.sub.Sub;

public class Main {
    public static void main(String... args) throws Exception {
        new Sub().sub();
    }
}

Normally, Sub is new to execute the method.

compile


#Compile main module
> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java
main\src\sample\main\Main.java:3:error:Package sample.sub cannot be displayed
import sample.sub.Sub;
             ^
  (Package sample.sub is the module sample_module.Declared in sub but not exported)
1 error

The sample.sub package is just ʻopens, not ʻexports. Therefore, if you try to refer to it statically at compile time, an error will occur.

To summarize the availability of reference at each timing,

Reference timing opens exports
At compile time ×
runtime

Like this.

open module ʻOpens is a setting for a specific package, but if you specify ʻopen module, ʻopens` is applied to the entire module.

Folder structure


|-main/
| `-src/
|   |-module-info.java
|   `-sample/main/
|     `-Main.java
`-sub/
  `-src/
    |-module-info.java
    `-sample/sub/
      |-foo/
      | `-Foo.java
      `-Sub.java

Foo.java


package sample.sub.foo;

public class Foo {
    public void foo() {
        System.out.println("Foo.foo()");
    }
}

sub/src/module-info.java


open module sample_module.sub {
    exports sample.sub;
}

--Adding ʻopenbefore the module --Onlysample.sub ʻexports, and nothing is set for the sample.sub.foo package. --Sub.java has not changed

Main.java


package sample.main;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import sample.sub.Sub;

public class Main {
    public static void main(String... args) throws Exception {
        new Sub().sub();

        Class<?> clazz = Class.forName("sample.sub.foo.Foo");
        Constructor<?> constructor = clazz.getConstructor();
        Object obj = constructor.newInstance();

        Method method = clazz.getMethod("foo");
        method.invoke(obj);
    }
}

--The Sub class is statically referenced to execute the method --The Foo class uses reflection to execute methods

Compile and run


#Compile sub module
> javac -d sub\class sub\src\module-info.java sub\src\sample\sub\Sub.java sub\src\sample\sub\foo\Foo.java

#Compile main module
> javac -p sub\class -d main\class main\src\module-info.java main\src\sample\main\Main.java

#Run
> java -p sub\class;main\class -m sample_module.main/sample.main.Main
Sub.sub()
Foo.foo()

--sample.sub.foo.Foo did not declare ʻopens or ʻexports, but it can be referenced from reflection. --By qualifying module with ʻopen, all the packages contained in that module will be qualified with ʻopens.

use, provides ServiceLoader can be defined in module-info.java from Java 9.

Implementation

Folder structure


|-user/
| `-src/
|   |-module-info.java
|   `-sample/user/
|     `-Main.java
|
`-provider/
  `-src/
    |-module-info.java
    `-sample/provider/
      |-api/
      | `-Foo.java
      `-impl/
        `-FooImpl.java

Foo.java


package sample.provider.api;

public interface Foo {
    void foo();
}

FooImpl.java


package sample.provider.impl;

import sample.provider.api.Foo;

public class FooImpl implements Foo {
    @Override
    public void foo() {
        System.out.println("FooImpl.foo()");
    }
}

provider/src/module-info.java


module sample_module.provider {
    exports sample.provider.api;
    provides sample.provider.api.Foo with sample.provider.impl.FooImpl;
}

Main.java


package sample.user;

import java.util.ServiceLoader;
import sample.provider.api.Foo;

public class Main {
    public static void main(String... args) {
        for (Foo foo : ServiceLoader.load(Foo.class)) {
            foo.foo();
        }
    }
}

user/src/module-info.java


module sample_module.user {
    requires sample_module.provider;
    uses sample.provider.api.Foo;
}

Operation check

compile


#Compiling provider module
> javac -d provider\class provider\src\module-info.java provider\src\sample\provider\api\Foo.java provider\src\sample\provider\impl\FooImpl.java

#Compile user module
> javac -d user\class -p provider\class user\src\module-info.java user\src\sample\user\Main.java

Run


> java -p provider\class;user\class -m sample_module.user/sample.user.Main
FooImpl.foo()

Description

provider/src/module-info.java


module sample_module.provider {
    exports sample.provider.api;
    provides sample.provider.api.Foo with sample.provider.impl.FooImpl;
}

user/src/module-info.java


module sample_module.user {
    requires sample_module.provider;
    uses sample.provider.api.Foo;
}

--The provider of "service provider [^ 1]" uses provides to specify the" service provider "for" service [^ 2] ". --provides <service> with <service provider>; --If you have multiple "service providers", you can list the "service providers" after with separated by commas. --The side that uses ServiceLoader uses ʻusesto specify the" service "to use. --ʻUses <service>; --The rest is used in the same way as the normal ServiceLoader

It seems that you should be aware that there are more declaration methods, such as when reading the code of the OSS framework.

[^ 1]: Class that implements "service" [^ 2]: Interfaces, abstract classes, etc.

jlink You can use the jlink command to create a Java runtime (subset) that contains only the bare minimum of modules. Even if JRE is not installed in the distribution destination, you can start the application by including the subset created by jlink.

Implementation

Folder structure


`-src/
  |-module-info.java
  `-sample/jlink/
    `-Main.java

Main.java


package sample.jlink;

public class Main {
    public static void main(String... args) {
        System.out.println("Hello jlink!!");
    }
}

module-info.java


module sample_module.jlink {
}

Create a subset

#compile
> javac -d class src\module-info.java src\sample\jlink\Main.java

#jar creation
> jar -c -f jar\module-jlink.jar -C class .

#Create runtime with jlink
> jlink -p .\jar;%JAVA_HOME%\jmods --add-modules sample_module.jlink --output .\output

--In the jlink command, specify at least the following - -p (--module-path) --Specify the path where the module to be included exists --The standard library modules are also required, so add the jmods folder under the JDK9 installation folder (% JAVA_HOME% ) to the module path as well. - --add-modules --It seems to specify the root module --I'm not sure what the root module is, but I think it's probably the starting point for building a graph of module dependencies. ――If you specify here, I think that the modules required for the Imozuru expression will be searched and incorporated into the runtime to be created. - --output --Specify the path of the output destination folder --If the folder already exists, an error will occur, so specify a path that does not exist.

The application with runtime is output under the ʻoutput` folder as shown below.

`-output/
  |-bin/
  |-conf/
  |-include/
  |-legal/
  |-lib/
  `-release

Under bin is the java.exe file, which is the java command for the subset.

#Go to the subset bin
> cd output\bin

#Check the included modules
> java --list-modules
java.base@9
sample_module.jlink

The subset contains only the minimum required modules (java.base and sample_module.jlink).

Try starting the program.

> java -m sample_module.jlink/sample.jlink.Main
Hello jlink!!

sample_module.jlink is built into the runtime, so you don't need to specify it with -p.

By the way, the size of the created runtime was 35.9 MB when I checked it with Windows Explorer.

Compress

Can be compressed to some extent as an option.

> jlink --compress=2 -p .\jar;%JAVA_HOME%\jmods --add-modules sample_module.jlink --output .\output

--Compression can be specified with --compress = N. --You can specify one of 0, 1, and 2 for N. --The help explains: (I'm not sure about Level 1)

-c, --compress=<0|1|2> Enable compression of resources:
                       Level 0: No compression
                       Level 1: Constant string sharing
                       Level 2: ZIP

--By the way, the size is now 24.2 MB

Generate a startup file

The need to specify the main class with -m at startup is not very good considering when distributing to people who are not familiar with Java. In that case, it is convenient to generate a script file for startup.

> jlink --launcher jlink-hello=sample_module.jlink/sample.jlink.Main  -p .\jar;%JAVA_HOME%\jmods --add-modules sample_module.jlink --output .\output

--Specify --launcher <command name> = <main class> --The start script specified by <command name> is output under ʻoutput \ bin`, so if you execute it, the program will start.

#Go under bin
> cd output\bin

#Run startup script
> jlink-hello.bat
Hello jlink!!

The generated script file has the following contents.

jlink-hello.bat


@echo off
set JLINK_VM_OPTIONS=
set DIR=%~dp0
"%DIR%\java" %JLINK_VM_OPTIONS% -m sample_module.jlink/sample.jlink.Main %*

A shell script for Linux was also output, but can it be started?

Examine dependent modules

You can use the jdeps command to find out which module the jar file depends on. When you add an existing non-module jar, you will know which module in the standard library to add.

As a test, check out Apache Commons lang-3.

> jdeps -s commons-lang3-3.6.jar
commons-lang3-3.6.jar -> java.base
commons-lang3-3.6.jar -> java.desktop

Feeding the jar with the -s option will show you the dependent modules.

java.desktop is a module that includes awt and swing, but there are beans and so on. I see.

If the jar is a named module, you need to specify the dependent module with --module-path (well, of course, it feels weird to have a dependent module to look up the dependent module. To do).

reference

-Latest Java Information Bureau-Create a module with Project Jigsaw ahead of Java SE 9: ITpro -Latest Java Information Bureau --Java SE 9, Project Jigsaw standard module and dependency description: ITpro -Latest Java Information Bureau --Customization of JRE by Project Jigsaw: ITpro

Recommended Posts

Jigsaw study notes
JavaFX study notes
Docker study notes
[Java] Study notes
Ruby study notes (puts)
Jigsaw notes in Gradle
Maven basic study notes
Ruby study notes (variables & functions)
Internal class and lambda study notes
JUnit 4 notes
Study policy
java notes
Spring Framework study notes [Part 1] DI container
synchronized notes