Project Jigsaw:Module System Quick-Start Guide

Project Jigsaw:Module System Quick-Start Guide

http://openjdk.java.net/projects/jigsaw/quick-start
这篇文章提供了一个简单的示例,让开发者上手modules.

lunix系统中,文件路径使用/,分隔符使用:。Windows系统中,文件路径使用, ;作为路径分隔符。我这里的操作是在windows系统上。

Greetings

第一个例子是名为com.greetings的module。 它能够简单的打印出“Greetings!”。这个module由两个源文件组成:module的声明(module-info.java)和main文件。

按照约定,模块的源码所在的目录是这个模块的名称。

src/com.greetings/com/greetings/Main.java

package com.greetings;
public class Main {
    public static void main(String[] args) {
        System.out.println("Greetings!");
    }
}

src/com.greetings/module-info.java

module com.greetings {}

使用如下的命令行,将源文件编译放到mods/com.greetins中:

E:>javac -d mods\com.greetings src\com.greetings\module-info.java src\com.gre
etings\com\greetings\Main.java
E:>

这里会自动创建mods\com.greetings文件目录,我们使用如下命令来运行:

E:>java –module-path mods -m com.greetings/com.greetings.Main
Greetings!
E:>

可以看到控制台输出了Greetings!
–module-path是module的路径一个或者多个包含modules的目录。
-m是可选项,它指定主模块,斜杠后面的值是module里面的主类名。

Greetings world

第二个例子修改了module声明,它声明了module org.astro.Module的依赖,同时输出org.astro的API.
src/org.astro/module-info.java

module org.astro{
    exports org.astro;
}

src/org.astro/org/astro/World.java

package org.astro;
public class World {
    public static String name() {
        return "world";
    }
}

src/com.greetins/com/greetings/Main.java

package com.greetings;
import org.astro.World;
public class Main{
    public static void main(String[] args){
        System.out.format("Greetings %s!%n", World.name());
    }
}

src/com.greetings/module-info.java

module org.astro {
    exports org.astro;
}

编译modules,一次编译一个module。使用javac命令编译module com.greetings 指定module的路径,它关联的module org.astro 和其他输出包的类型都会被关联到。

E:\07_Learn\java9 eaxmple>javac -d mods\org.astro src\org.astro\org\astro\World.java

E:\07_Learn\java9 eaxmple>javac -d mods\org.astro src\org.astro\module-info.java

E:\07_Learn\java9 eaxmple>javac --module-path mods -d mods\com.greetings src\com.greetings\module-in
fo.java

E:\07_Learn\java9 eaxmple>javac --module-path mods -d mods\com.greetings src\com.greetings\com\greet
ings\Main.java

E:\07_Learn\java9 eaxmple>java --module-path mods -m com.greetings/com.greetings.Main
Greetings world!

在编译module org.astro的时候有一个要注意的地方。因为在module的声明里面有输出org.astro这个包,所以要先编译这个包下的内容,即先编译World.java,否则就会报如下错误。

E:\07_Learn\java9 eaxmple>javac -d mods\org.astro src\org.astro\module-info.java
src\org.astro\module-info.java:2: 错误: 程序包为空或不存在: org.astro
    exports org.astro;
               ^
1 个错误

最后的目录结构如下

├─mods
│  ├─com
│  │  └─greetings
│  │      │  module-info.class
│  │      │  
│  │      └─com
│  │          └─greetings
│  │                  Main.class
│  │                  
│  └─org.astro
│      │  module-info.class
│      │  
│      └─org
│          └─astro
│                  World.class
│                  
└─src
    ├─com.greetings
    │  │  module-info.java
    │  │  
    │  └─com
    │      └─greetings
    │              Main.java
    │              
    └─org.astro

Multi-module compilation

在上一个例子中,我们将module com.greetings 和module org.astro分开编译的。
在linux中,可以使用如下命令:

$ mkdir mods

$ javac -d mods --module-source-path src $(find src -name "*.java")

$ find mods -type f
mods/com.greetings/com/greetings/Main.class
mods/com.greetings/module-info.class
mods/org.astro/module-info.class
mods/org.astro/org/astro/World.class

在windos中查找文件的命令为

E:\07_Learn\java9 eaxmple>for /r src %i in (*.java) do @echo %i
E:\07_Learn\java9 eaxmple\src\com.greetings\module-info.java
E:\07_Learn\java9 eaxmple\src\com.greetings\com\greetings\Main.java
E:\07_Learn\java9 eaxmple\src\org.astro\module-info.java
E:\07_Learn\java9 eaxmple\src\org.astro\org\astro\World.java

但是一次性编译多个模块没有成功。

Packaging

到现在,编译的module内容都是放在文件系统里面的。出于运输和部署的目的,将module打包成模块化的jar更便利。模块化的JAR是常规的jar文件,在它的顶级目录下有一个module-info.class文件。下面的例子生成了org.astro@1.0.jar和com.greetings.jar,放在mlib文件夹下。

E:\07_Learn\java9 eaxmple>jar --create --file=mlib\org.astro@1.0.jar --module-version=1.0 -C mods\or
g.astro .

E:\07_Learn\java9 eaxmple>jar --create --file=mlib\com.greetings.jar --main-class=com.greetings.Main
 -C mods\com.greetings .

E:\07_Learn\java9 eaxmple>dir mlib
 驱动器 E 中的卷是 文档
 卷的序列号是 0005-F6CF

 E:\07_Learn\java9 eaxmple\mlib 的目录

2018/03/08 周四  15:00    <DIR>          .
2018/03/08 周四  15:00    <DIR>          ..
2018/03/08 周四  15:00             1,357 com.greetings.jar
2018/03/08 周四  14:58             1,152 org.astro@1.0.jar
               2 个文件          2,509 字节
               2 个目录 248,652,009,472 可用字节

使用jar –help可以看到jar命令的使用方法。

在这个例子里面,module org.astro打包指明了它的版本是1.0。Module com.greetings也被打包并指明它的主类是com.greetings.Main。我们可以执行module com.greetings 而不需要指明主类。

E:\07_Learn\java9 eaxmple>java -p mlib -m com.greetings
Greetings world!

这个命令行里面的-p也可以换成–module-path,都表示模块路径。

可以通过jar –help来看jar命令的其他选项,其中有一个是打印被打成jar包的module的模块声明。

E:\07_Learn\java9 eaxmple>jar --describe-module --file=mlib\org.astro@1.0.jar
org.astro@1.0 jar:file:///E:/07_Learn/java9%20eaxmple/mlib/org.astro@1.0.jar/!module-info.class
exports org.astro
requires java.base mandated

Missing requires or missing exports

我们把上个例子中com.greetings模块里面的requires注释掉,看看会发生什么。
src\com.greetings\module-info.java

module com.greetings{
     // requires org.astro;
}
E:\07_Learn\java9 eaxmple>javac -p mods -d mods\com.greetings src\com.greetings\module-info.java src
\com.greetings\com\greetings\Main.java
src\com.greetings\com\greetings\Main.java:2: 错误: 程序包 org.astro 不可见
import org.astro.World;
          ^
  (程序包 org.astro 已在模块 org.astro 中声明, 但模块 com.greetings 未读取它)
1 个错误

我们现在再来把这个bug修复,但是将org.astro模块声明里面的exports去掉:
src\com.greetings\module-info.java

module com.greetings{
     requires org.astro;
}

src\org.astro\module-info.java

module org.astro{
    // exports org.astro;
}
E:\07_Learn\java9 eaxmple>javac -d mods\org.astro src\org.astro\module-info.java src\org.astro\org\a
stro\World.java

E:\07_Learn\java9 eaxmple>javac -p mods -d mods\com.greetings src\com.greetings\module-info.java src
\com.greetings\com\greetings\Main.java
src\com.greetings\com\greetings\Main.java:2: 错误: 程序包 org.astro 不可见
import org.astro.World;
          ^
  (程序包 org.astro 已在模块 org.astro 中声明, 但该模块未导出它)
1 个错误

Services

服务允许服务使用者和服务提供者之间的松散耦合。
这个例子里面有一个服务消费者和一个服务提供者:
- module com.socket 提供了network sockets的API. 这个API在com.socket包里面,所以这个包是输出的。这个API是可插拔的,可以替代实现。这个服务类是同一个module里面的com.socket.spi.NetworkSocketProvider类。因此包com.socket.spi也是输出的。
- module org.fastsocket 是一个服务提供者模块。它提供了com.socket.spi.NetworkSocketProvider的实现。它并不输出任何包。

下面是com.socket的源码。
src\com.socket\module-info.java

module com.socket{
    exports com.socket;
    exports com.socket.spi;
    uses com.socket.spi.NetworkSocketProvider;
}

src\com.socket\com\socket\spi\NetworkSocketProvider.java

package com.socket.spi;

import com.socket.NetworkSocket;

public abstract class NetworkSocketProvider{
    protected NetworkSocketProvider(){}

    public abstract NetworkSocket openNetworkSocket();
}

src\com.socket\com\socket\NetworkSocket.java

package com.socket;

import java.io.Closeable;
import java.util.Iterator;
import java.util.ServiceLoader;

import com.socket.spi.NetworkSocketProvider;

public abstract class NetworkSocket implements Closeable{
    protected NetworkSocket(){}

    public static NetworkSocket open(){
        ServiceLoader<NetworkSocketProvider> sl = ServiceLoader.load(NetworkSocketProvider.class);
        Iterator<NetworkSocketProvider> iter = sl.iterator();
        if (!iter.hasNext()){
            throw new RuntimeException("No service provider found!"); 
        }
        NetworkSocketProvider provider = iter.next();
        return provider.openNetworkSocket();
    }
} 

下面是module org.fastsocket的源码。
src\org.fastsocket\module-info.java

module org.fastsocket {
    requires com.socket;
    proviides com.socket.spi.NetworkSocketProvider with org.fastsocket.FastNetworkSocketProvider;
}

src\org.fastsocket\org\fastsocket\FastNetworkSocketProvider.java

package org.fastsocket;

import com.socket.NetworkSocket;
import com.socket.spi.NetworkSocketProvider;

public class FastNetworkSocketProvider extends NetworkSocketProvider {
    public FastNetworkSocketProvider(){}

    @Override
    public NetworkSocket openNetworkSocket(){
        return new FastNetworkSocket();
    }
}

src\org.fastsocket\org\fastsocket\FastNetworkSocket.java

package org.fastsocket;

import com.socket.NetworkSocket;

class FastNetworkSocket extends NetworkSocket{
    FastNetworkSocket(){}
    public void close(){}
}

然后将所有文件都编译

E:\07_Learn\java9 eaxmple>javac -d mods\com.socket src\com.socket\module-info.java src\com.socket\co
m\socket\spi\NetworkSocketProvider.java src\com.socket\com\socket\NetworkSocket.java
E:\07_Learn\java9 eaxmple>javac --module-path mods -d mods\org.fastsocket src\org.fastsocket\org\fas
tsocket\FastNetworkSocketProvider.java src\org.fastsocket\org\fastsocket\FastNetworkSocket.java  src
\org.fastsocket\module-info.java

最后再来修改com.greetings,让它来使用API.
src\com.greetins\module-info.java

module com.greetings{
     requires com.socket;
}

src\com.greetings\com\greetins\Main.java

package com.greetings;

import com.socket.NetworkSocket;

public class Main{
    public static void main(String[] args){
        NetworkSocket s = NetworkSocket.open();
        System.out.println(s.getClass());
    }
}

编译

E:\07_Learn\java9 eaxmple>javac -d mods\com.greetings -p mods src\com.greetings\*.java src\com.greet
ings\com\greetings\*.java

最后来运行:

E:\07_Learn\java9 eaxmple>java -p mods -m com.greetings/com.greetings.Main
class org.fastsocket.FastNetworkSocket

E:\07_Learn\java9 eaxmple>

The linker

jlink 是一个链接器工具,它能用来链接模块集以及它们的传递依赖,还能自定义创建模块运行时映像(参见JEP 220)。

目前,这个工具要求被链接的模块是模块化的jar或JDOM格式的。JDK是以JDOM格式来构建包的。

下面的这个例子创建了一张运行时映像,包括com.greetings模块以及传递性的依赖:

E:\07_Learn\java9 eaxmple>jlink --module-path %JAVA_HOME%\jmods;mlib --add-modules com.greetings --o
utput greetingsapp

E:\07_Learn\java9 eaxmple>

linux环境下要写成

jlink --module-path $JAVA_HOME/jmods:mlib --add-modules com.greetings --output greetingsapp

–module-path 打包了的模块的路径
%JAVA_HOME%\jmods 是包含java.base.jmod,其他标准还有JDK模块的文件夹。
mlib文件夹包含com.greetings模块构建。
jlink工具支持许多高级选项来定制生成的映像,参见jlink –help获取更多选项。

–patch-module

以前开发者们修改类,例如java.util.concurrent类,会先从CVS检出,然后编译源码,最后使用-Xbootclasspath/p部署。

现在,-Xbootclasspath/p 已经被废弃了,它的模块被–patch-module代替了,用来覆盖模块中的类。它也可以用来想模块里面增加文件。javac命令也支持–patch-module选项。

下面是编译java.util.concurrent.ConcurrentHashMap的一个新版本并且在运行时使用它的例子

javac --patch-module java.base=src -d mypatches/java.base \
        src/java.base/java/util/concurrent/ConcurrentHashMap.java

java --patch-module java.base=mypatches/java.base ...

More information

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zerlinda_Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值