美文网首页
Java Platform Module System

Java Platform Module System

作者: yunpxu | 来源:发表于2018-11-23 17:00 被阅读0次

Goals of java platform module system

  • Reliable configuration, use module path to replace class path
  • Strong encapsulation, control visibility of public classes in a module

All source code is available at github.

1 Defining modules

A module can contain java class, interface, xml and json file etc.
module-info.java should be at the root of module's source file hierarchy.

1.1 Module declaration

Define a module com.foo.bar

module com.foo.bar { }

Module com.foo.bar depends on module org.baz.qux at compile time and run time

module com.foo.bar {
    requires org.baz.qux;
}

Public classes in package com.foo.bar.alpha and com.foo.bar.beta are visible to other modules

module com.foo.bar {
    requires org.baz.qux;
    exports com.foo.bar.alpha;
    exports com.foo.bar.beta;
}

1.2 Module artifacts

A modular jar is a regular jar with module-info.class
JMOD - java platform class files are packed in jmod format

[yunpxu@yunpxu-mac Home]$ jmod list /Library/Java/JavaVirtualMachines/jdk-11.0.1.jdk/Contents/Home/jmods/java.base.jmod
...
classes/java/lang/Boolean.class
classes/java/lang/BootstrapMethodError.class
classes/java/lang/Byte$ByteCache.class
classes/java/lang/Byte.class
classes/java/lang/Character$CharacterCache.class
classes/java/lang/Character$Subset.class
classes/java/lang/Character$UnicodeBlock.class
classes/java/lang/Character$UnicodeScript.class
classes/java/lang/Character.class
...
classes/java/lang/Class.class
...

1.3 Module descriptor

An IDE or a build-time packaging tool can insert attributes such as a module’s version, title, description, and license. This information can be read at compile time and run time via the module system’s reflection facilities.

1.4 Platform module

Module java.base, base module doesn't depend on any other module, every other module depends on base module implicitly.
Java SE platform specification module names start with 'java', JDK specific module names start with 'jdk'.

2 Using Modules

2.1 Module path

Module path vs class path, system locate the whole modules in the module path rather than individual classes.

2.2 Resolution

Resolution is the process of computing how each module depends on each other.
Module com.foo.app depends on module com.foo.bar and platform module java.sql

module com.foo.app {
    requires com.foo.bar;
    requires java.sql;
}

Platform module java.sql

module java.sql {
    requires java.logging;
    requires java.xml;
    exports java.sql;
    exports javax.sql;
    exports javax.transaction.xa;
}

Module graph for com.foo.app
Dark blue lines represent explicit dependence(requires), the light blue lines represent implicit dependence over base module.

module graph.png

2.2 Readability

The module system ensures every dependence is fulfilled by one and only one other module.

  • There is no dependence relationship between module a and b, then we can define package com.foo in both module a and b.
  • If module a depends on module b, then we can't define package com.foo in both module a and b.
define unique package in modules.png

2.3 Accessibility

Module com.foo.app can access

  • Packages exported by module com.foo.bar and java.sql
  • Packages exported by module java.base

Module com.foo.app can't access

  • Packages exported by module org.baz.qux and java.xml and java.logging
module exports.png

2.4 Implied readability

With requires transitive, module com.foo.app can also access module java.xml and java.logging

module java.sql {
    requires transitive java.logging;
    requires transitive java.xml;
    exports java.sql;
    exports javax.sql;
    exports javax.transaction.xa;
}
Implied readability.png

3 Compatibility & migration

3.1 The unnamed module

Jar files on class path are considered as the unnamed module.

  • The unnamed module reads every other modules
  • The unnamed module exports all of its packages
  • A named module can't depend on the unnamed module
  • If a package is defined in both a named module and the unnamed module then the package in the unnamed module is ignored.
How JVM load a class.png

3.2 Bottom-up migration

We'll build our jars without module-info.

[yunpxu@yunpxu-mac JavaModule]$ tree out/production/
out/production/
├── app
│   ├── com
│   │   └── foo
│   │       └── app
│   │           └── App.class
│   └── module-info.class
├── bar
│   ├── com
│   │   └── foo
│   │       └── bar
│   │           └── Bar.class
│   └── module-info.class
└── qux
    ├── module-info.class
    └── org
        └── baz
            └── qux
                └── Qux.class

12 directories, 6 files


[yunpxu@yunpxu-mac JavaModule]$ cd out/artifacts/

[yunpxu@yunpxu-mac artifacts]$ jar cvf org-baz-qux.jar -C ../production/qux org/baz/qux/
added manifest
adding: org/baz/qux/(in = 0) (out= 0)(stored 0%)
adding: org/baz/qux/Qux.class(in = 457) (out= 309)(deflated 32%)


[yunpxu@yunpxu-mac artifacts]$ jar cvf com-foo-bar.jar -C ../production/bar com/foo/bar/
added manifest
adding: com/foo/bar/(in = 0) (out= 0)(stored 0%)
adding: com/foo/bar/Bar.class(in = 916) (out= 562)(deflated 38%)


[yunpxu@yunpxu-mac artifacts]$ jar cvfe com-foo-app.jar com.foo.app.App -C ../production/app com/foo/app/
added manifest
adding: com/foo/app/(in = 0) (out= 0)(stored 0%)
adding: com/foo/app/App.class(in = 422) (out= 278)(deflated 34%)

[yunpxu@yunpxu-mac artifacts]$ java -cp 'com-foo-app.jar:com-foo-bar.jar:org-baz-qux.jar' com.foo.app.App
Bar
Qux

[yunpxu@yunpxu-mac artifacts]$ tree
.
├── com-foo-app.jar
├── com-foo-bar.jar
└── org-baz-qux.jar

0 directories, 3 files

The initial module graph would look like this

Initial module graph.png

We can use jdeps to inspect class dependencies, org-baz-qux.jar depends on java.base only.

[yunpxu@yunpxu-mac artifacts]$ jdeps org-baz-qux.jar 
org-baz-qux.jar -> java.base
   org.baz.qux                                        -> java.io                                            java.base
   org.baz.qux                                        -> java.lang                                          java.base

So we can improve the module graph by declaring module org.baz.qux

migrate-1.png

com-foo-bar.jar depends on java.base, java.sql, java.xml and org.baz.qux which is identified as the unnamed module.

[yunpxu@yunpxu-mac artifacts]$ jdeps com-foo-bar.jar 
com-foo-bar.jar -> java.base
com-foo-bar.jar -> java.sql
com-foo-bar.jar -> java.xml
com-foo-bar.jar -> not found
   com.foo.bar                                        -> java.io                                            java.base
   com.foo.bar                                        -> java.lang                                          java.base
   com.foo.bar                                        -> java.sql                                           java.sql
   com.foo.bar                                        -> javax.xml.parsers                                  java.xml
   com.foo.bar                                        -> org.baz.qux                                        not found

com-foo-app.jar depends on java.base and com.foo.bar which is identified as the unnamed module.

[yunpxu@yunpxu-mac artifacts]$ jdeps com-foo-app.jar 
com-foo-app.jar -> java.base
com-foo-app.jar -> not found
   com.foo.app                                        -> com.foo.bar                                        not found
   com.foo.app                                        -> java.lang                                          java.base

We can get the migrated module graph, since we know com.foo.app depends on com.foo.bar and com.foo.bar depends on org.baz.qux

migrate-2.png

3.3 Automatic modules

Jar files on module path but without module declaration are considered automatic modules.

  • Automatic module's name is derived from jar file name
  • Automatic module can be read by other modules
  • Automatic module can read every other module
  • All packages in automatic module are exposed
com.foo.bar and org.baz.qux are automatic modules

3.4 Bridges to the class path

Two jar files on the class path contain classes in the same package.

  • Try to remove one of the jar file, and put the other one in module path(automatic module)
  • If these two jars are needed, then leave them in class path

4 Services

Service interface

module com.service {
    exports com.service
}

Service implementation

module com.service.impl {
  requires com.service;
  provides com.service.MyService with
       com.com.service.impl.MyServiceImpl
}

Service client

module com.client{
    requires com.service;
    uses com.service.MyService;
}
Iterable<MyService> services =
         ServiceLoader.load(MyService.class);

5 Advanced topics

5.1 Reflection

An instance of java.lang.Module class represents a single module at run time.
An instance of java.lang.module.ModuleDescriptor class represents module descriptors.

Module java_sql = Driver.class.getModule();
System.out.println(java_sql);
System.out.println(java_sql.getDescriptor());

//output
module java.sql
module { name: java.sql@11.0.1, [transitive java.transaction.xa, mandated java.base, transitive java.xml, transitive java.logging], uses: [java.sql.Driver], exports: [javax.sql, java.sql] }

//module-info.java
module java.sql {
    requires transitive java.logging;
    requires transitive java.transaction.xa;
    requires transitive java.xml;

    exports java.sql;
    exports javax.sql;

    uses java.sql.Driver;
}

5.2 Reflective readability

The reflection API simply assume that any code that reflects upon some type is in a module that can read the module that defines that type.

String providerName
    = System.getProperty("javax.xml.stream.XMLInputFactory");
if (providerName != null) {
    Class providerClass = Class.forName(providerName, false,
                                        Thread.getContextClassLoader());
    XMLInputFactory.class.getModule()
                               .addReads(providerClass.getModule());
    Object ob = providerClass.newInstance();
    return (XMLInputFactory)ob;
}
// Otherwise use ServiceLoader

5.3 Class loaders

  • Bootstrap and extension class loader load classes from platform modules
  • Application class loader load classes from module path

5.4 Unnamed modules

  • ClassLoader.getUnnamedModule()

5.5 Layers

Layer is created from

  1. A graph of modules in a Configuration
  2. A function that maps each module to a ClassLoader
[yunpxu@yunpxu-mac JavaModule]$ tree out/production/
out/production/
├── JavaModule
│   ├── AppTest$1.class
│   └── AppTest.class
├── app
│   ├── com
│   │   └── foo
│   │       └── app
│   │           └── App.class
│   └── module-info.class
├── bar
│   ├── com
│   │   └── foo
│   │       └── bar
│   │           └── Bar.class
│   └── module-info.class
└── qux
    ├── module-info.class
    └── org
        └── baz
            └── qux
                └── Qux.class

13 directories, 8 files
//boot layer
ModuleLayer bootLayer = ModuleLayer.boot();
//boot configuration
Configuration bootCfg = bootLayer.configuration();

//path of the module
ModuleFinder finder = ModuleFinder.of(Paths.get("/Users/yunpxu/IdeaProjects/JavaModule/out/production"));
//configuration for module com.foo.app
Configuration appCfg = bootCfg.resolve(finder, ModuleFinder.of(), Set.of("com.foo.app"));
//print dependence for module com.foo.app
appCfg.modules().stream().forEach(module1 -> {
    System.out.format("%s -> %s%n", module1.name(), module1.reads().stream().map(ResolvedModule::name).collect(Collectors.joining(", ")));
});

//AppClassLoader->PlatformClassLoader->null
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

//create a new layer for module com.foo.app
ModuleLayer appLayer = bootLayer.defineModulesWithOneLoader(appCfg, systemClassLoader);
ClassLoader appClassLoader = appLayer.findLoader("com.foo.app");
ClassLoader barClassLoader = appLayer.findLoader("com.foo.bar");
ClassLoader quxClassLoader = appLayer.findLoader("org.baz.qux");
System.out.println("defineModulesWithOneLoader");
System.out.format("com.foo.app %s %s%n", appClassLoader, appClassLoader.getParent());
System.out.format("com.foo.bar %s %s%n", barClassLoader, barClassLoader.getParent());
System.out.format("org.baz.qux %s %s%n", quxClassLoader, quxClassLoader.getParent());

appLayer = bootLayer.defineModulesWithManyLoaders(appCfg, systemClassLoader);
appClassLoader = appLayer.findLoader("com.foo.app");
barClassLoader = appLayer.findLoader("com.foo.bar");
quxClassLoader = appLayer.findLoader("org.baz.qux");
System.out.println("defineModulesWithManyLoaders");
System.out.format("com.foo.app %s %s%n", appClassLoader, appClassLoader.getParent());
System.out.format("com.foo.bar %s %s%n", barClassLoader, barClassLoader.getParent());
System.out.format("org.baz.qux %s %s%n", quxClassLoader, quxClassLoader.getParent());

Function<String, ClassLoader> classLoaderFunc = (m) -> {
    if (m.contains("foo")) {
        return systemClassLoader;
    } else {
        return new ClassLoader() {
        };//dummy class loader
    }
};
appLayer = bootLayer.defineModules(appCfg, classLoaderFunc);
appClassLoader = appLayer.findLoader("com.foo.app");
barClassLoader = appLayer.findLoader("com.foo.bar");
quxClassLoader = appLayer.findLoader("org.baz.qux");
System.out.println("defineModulesWithCustomLoaders");
System.out.format("com.foo.app %s %s%n", appClassLoader, appClassLoader.getParent());
System.out.format("com.foo.bar %s %s%n", barClassLoader, barClassLoader.getParent());
System.out.format("org.baz.qux %s %s%n", quxClassLoader, quxClassLoader.getParent());
Module Dependency
com.foo.app com.foo.bar, java.base
com.foo.bar java.sql, java.transaction.xa, java.base, java.xml, java.logging, org.baz.qux
org.baz.qux java.base

defineModulesWithOneLoader

Module ClassLoader Parent ClassLoader
com.foo.app jdk.internal.loader.Loader@68be2bc2 jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17
com.foo.bar jdk.internal.loader.Loader@68be2bc2 jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17
org.baz.qux jdk.internal.loader.Loader@68be2bc2 jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17

defineModulesWithManyLoaders

Module ClassLoader Parent ClassLoader
com.foo.app jdk.internal.loader.Loader@c038203 jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17
com.foo.bar jdk.internal.loader.Loader@8bd1b6a jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17
org.baz.qux jdk.internal.loader.Loader@18be83e4 jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17

defineModulesWithCustomLoaders

Module ClassLoader Parent ClassLoader
com.foo.app jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17 jdk.internal.loader.ClassLoaders$PlatformClassLoader@4501b7af
com.foo.bar jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17 jdk.internal.loader.ClassLoaders$PlatformClassLoader@4501b7af
org.baz.qux AppTest$1@d8355a8 jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17

5.6 Qualified exports

Exports sun.reflect to all modules.

module java.base {
    ...
    exports sun.reflect;
}

With exports sun.reflect to, sun.reflect is exported to specified modules only.

module java.base {
    ...
    exports sun.reflect to
        java.corba,
        java.logging,
        java.sql,
        java.sql.rowset,
        jdk.scripting.nashorn;
}

6 Reference

The State of the Module System
Idea getting-started-with-java-9-module-system

相关文章

网友评论

      本文标题:Java Platform Module System

      本文链接:https://www.haomeiwen.com/subject/nrucfqtx.html