Java Platform Module System

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,322评论 0 10
  • 小时候,外公,外婆 ,我,烟。外婆不让外公吸烟,他烟瘾太大,后来嗓子已经坏了,肺里呼呼啦啦的响,我就把烟藏...
    倔强的牛头梗阅读 390评论 0 0