资讯专栏INFORMATION COLUMN

Java9特性预览Jigsaw:模块化系统快速入门指南

dack / 2690人阅读

摘要:例子中的文件路径使用斜杠,路径分隔符是冒号。到目前为止的示例中,已编译的模块的内容在文件系统上以分散的文件的形式存储。当用于分发和部署时通常更方便的方式是将一个模块打包成一个模块化的。在这个例子中,模块打包时表明了它的版本是。

本文档提供了几个简单的例子,让开发人员开始使用模块。

例子中的文件路径使用斜杠,路径分隔符是冒号。使用微软Windows开发的人员应该使用文件路径以反斜杠和一个分号作为路径分隔符。

Greetings

第一个例子是一个只打印“Greetings!”命名为com.greetings的模块。该模块包括两个源文件:模块声明文件(module-info.java)和Main.java

按惯例,模块的源代码在一个目录中,该目录是模块的名称:

    src/com.greetings/com/greetings/Main.java
    src/com.greetings/module-info.java
    
    $ cat src/com.greetings/module-info.java
    module com.greetings { }
    
    $ cat src/com.greetings/com/greetings/Main.java
    package com.greetings;
    public class Main {
        public static void main(String[] args) {
            System.out.println("Greetings!");
        }
    }

使用以下命令,将源码编译到 mods/com.greetings 目录下:

    $ mkdir -p mods/com.greetings

    $ javac -d mods/com.greetings src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java

现在我们用下面的命令运行这个例子:

    $ java -modulepath mods -m com.greetings/com.greetings.Main

-modulepath是模块的路径,它的值是一个或多个包含模块的目录。-m 选项指定了主模块,在/后的值是模块主类包含包名的完整名称。

Greetings world

第二个示例是在之前示例的基础上增加了org.astro模块依赖,模块org.astro提供org.astro包的API。

    src/org.astro/module-info.java
    src/org.astro/org/astro/World.java
    src/com.greetings/com/greetings/Main.java
    src/com.greetings/module-info.java
    
    $ cat src/org.astro/module-info.java
    module org.astro {
        exports org.astro;
    }
    
    $ cat src/org.astro/org/astro/World.java
    package org.astro;
    public class World {
        public static String name() {
            return "world";
        }
    }
    
    $ cat src/com.greetings/module-info.java
    module com.greetings {
        requires org.astro;
    }
    
    $ cat src/com.greetings/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());
        }
    }

该模块编译,在同一时间。使用javac命令指定模块路径编译模块com.greetings,并有模块org.astro的引用,这样可以获取到org.astro提供的API。

    $ mkdir mods/org.astro mods/com.greetings

    $ javac -d mods/org.astro src/org.astro/module-info.java src/org.astro/org/astro/World.java
    
    $ javac -modulepath mods -d mods/com.greetings src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java                              

该示例以与第一个示例完全相同的方式运行:

    $ java -modulepath mods -m com.greetings/com.greetings.Main
    Greetings world!
多模块编译

在前面的示例,模块 com.greetings 和模块 org.astro 是分别编译的。使用javac 命令编译模块时还可以一次编译多个模块。

    $ mkdir mods

      $ javac -d mods -modulesourcepath 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
Packaging

到目前为止的示例中,已编译的模块的内容在文件系统上以分散的文件的形式存储。当用于分发和部署时通常更方便的方式是将一个模块打包成一个模块化的JAR。一个模块化的JAR是一个普通的jar文件中在顶级目录有一个module-info.class。下面的示例在mlib目录org.astro@1.0.jar和com.greetings.jar。

    $ mkdir mlib

    $ jar --create --file=mlib/org.astro@1.0.jar --module-version=1.0 -C mods/org.astro .
    
    $ jar --create --file=mlib/com.greetings.jar --main-class=com.greetings.Main -C mods/com.greetings .
    
    $ ls mlib
    com.greetings.jar   org.astro@1.0.jar

在这个例子中,org.astro模块打包时表明了它的版本是1.0 (--module-version=1.0)。模块com.greetings在打包时表明其主类是com.greetings.Main (--main-class=com.greetings.Main)。我们现在可以执行模块com.greetings而无需指定其主类:

    $ java -mp mlib -m com.greetings
    Greetings world!

上面的命令中使用了 -modulepath 的简写 -mp

jar工具增加了很多新的选项(可以通过jar -help查看),其中之一是打印一个模块jar的模块声明:

原文中给出的是:

    $ jar --print-module-descriptor --file=mlib/org.astro@1.0.jar

    Name:
      org.astro@1.0
    Requires:
      java.base [ MANDATED ]
    Exports:
      org.astro

但我实际在Windows下测试的结果为:

C:UserscoderknockworkspaceJDK9>jar --print-module-descriptor --file=mliborg.astro@1.0.jar

org.astro@1.0
  requires mandated java.base
  exports org.astro
C:UserscoderknockworkspaceJDK9>jar --print-module-descriptor --file=mlibcom.greetings.jar
com.greetings
  requires mandated java.base
  requires org.astro
  conceals com.greetings
  main-class com.greetings.Main
缺少requires(依赖) 或者exports(输出)

如果我们在上一个示例中在com.greetings module的模块声明中没有设定依赖org.astro,让我们看看会发生什么:

    $ cat src/com.greetings/module-info.java
    module com.greetings {
        // requires org.astro;
    }

    $ javac -modulepath 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;
                    ^
    src/com.greetings/com/greetings/Main.java:5: 错误: 找不到符号
            System.out.format("Greetings %s!%n", World.name());
                                                 ^
      符号:   变量 World
      位置: 类 Main
    2 个错误

现在我们试一下,com.greetings声明了依赖,但是org.astro没有声明输出会发生什么:

    $ cat src/com.greetings/module-info.java
    module com.greetings {
        requires org.astro;
    }
    $ cat src/org.astro/module-info.java
    module org.astro {
        // exports org.astro;
    }

    $ javac -modulepath 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;
                    ^
    src/com.greetings/com/greetings/Main.java:5: 错误: 找不到符号
            System.out.format("Greetings %s!%n", World.name());
                                                 ^
      符号:   变量 World
      位置: 类 Main
    2 个错误
服务

服务允许服务消费者模块和服务提供商模块之间的松散耦合。

这个例子有一个服务消费模块和一个服务提供模块:

模块com.socket输出了一个网络套接字的API。API被封装在com.socket包,所以这个包是输出者。API是可插拔的,允许替换具体的实现。com.socket.spi.networksocketprovider是实际提供服务的抽象类,所以com.socket.spi也应该被输出。

org.fastsocket模块是一个服务提供模块,它提供了一个com.socket.spi.NetworkSocketProvider的实现,不需要输出。

下面是 com.socket 模块的源码:

    $ cat src/com.socket/module-info.java
    module com.socket {
        exports com.socket;
        exports com.socket.spi;
        uses com.socket.spi.NetworkSocketProvider;
    }

    $ cat 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 sl
                = ServiceLoader.load(NetworkSocketProvider.class);
            Iterator iter = sl.iterator();
            if (!iter.hasNext())
                throw new RuntimeException("No service providers found!");
            NetworkSocketProvider provider = iter.next();
            return provider.openNetworkSocket();
        }
    }


    $ cat 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();
    }

下面是org.fastsocket模块的源码:

    $ cat src/org.fastsocket/module-info.java
    module org.fastsocket {
        requires com.socket;
        provides com.socket.spi.NetworkSocketProvider
            with org.fastsocket.FastNetworkSocketProvider;
    }

    $ cat 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();
        }
    }

    $ cat src/org.fastsocket/org/fastsocket/FastNetworkSocket.java
    package org.fastsocket;

    import com.socket.NetworkSocket;

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

为了简单起见,我们一起编译这两个模块。在实践中,服务消费模块和服务提供模块几乎总是多带带编译。

    $ mkdir mods
    $ javac -d mods -modulesourcepath src $(find src -name "*.java")

最后我们修改一下com.greetings模块,在其Main中使用上面提供出的API:

    $ cat src/com.greetings/module-info.java
    module com.greetings {
        requires com.socket;
    }

    $ cat src/com.greetings/com/greetings/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());
        }
    }


    $ javac -d mods/com.greetings/ -mp mods $(find src/com.greetings/ -name "*.java")

最后,我们运行一下:

    $ java -mp mods -m com.greetings/com.greetings.Main
    class org.fastsocket.FastNetworkSocket

输出结果表明服务提供者已经找到,而且使用的是NetworkSocket工厂,实现是FastNetworkSocketProvider。

链接器

jlink 是连接器工具,用来连接一组模块 ,连同他们的依赖关系,创建一个自定义模块运行时镜像( JEP 220规范定义)。

该工具目前需要的封装成模块JAR或者JMOD格式的模块的路径。JDK中将一些标准以及JDK特性包装成JMOD格式(在JDK安装目录的jmods目录下)。

下面的示例创建一个运行时镜像包含模块com.greetings以及传递相关的依赖:

    jlink --modulepath $JAVA_HOME/jmods:mlib --addmods com.greetings --output greetingsapp

--modulepath 是包含将要打包的模块的模块路径(示例中$JAVA_HOME/jmods是JDK内置的模块,所有模块默认引入有java.base模块的依赖,java.base在$JAVA_HOME/jmods中),Linux目录分隔符是:Windows是;按照自己的环境修改“jmods:mlib”的符号。(我在本地测试这个没有成功,有成功的同学请留言)

$JAVA_HOME/jmods是java.base.jmod的模块路径同时包含别的JDK模块.。如果你是自己编译的OpenJDK,那么应该使用 $BUILDOUTPUT/images/jmods, $BUILDOUTPUT 指的是你设置的编译输出目录。

在模块路径设置里的目录mlib包含模块com.greetings。

jlink工具支持许多高级选项自定义生成的镜像, jlink --help查看更多选项。

我是广告

本人的直播课程在 7 月份就要开始了,希望小伙伴们支持一下,现在报名有优惠噢

https://segmentfault.com/l/15...

https://segmentfault.com/l/15...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67218.html

相关文章

  • Java8 和 Java9 的主要新特性

    摘要:的主要新特性表达式允许把函数作为一个方法的参数传递进方法中。作用解决被诟病的匿名内部类的问题。新特性模块系统模块是一个包的容器,最大的变化之一是引入模块系统。支持标准标准是协议的最新版本,新的支持和流以及服务器推送特性。 Java 8 的主要新特性 1. Lambda 表达式 Lambda 允许把函数作为一个方法的参数传递进方法中。 作用:解决 Java 被诟病的匿名内部类的问题。 2...

    SmallBoyO 评论0 收藏0
  • 《Java应用架构设计:块化模式与OSGi》读书笔记

    摘要:本书概括以软件系统为例,重点讲解了应用架构中的物理设计问题,即如何将软件系统拆分为模块化系统。容器独立模块不依赖于具体容器,采用轻量级容器,如独立部署模块可独立部署可用性模式发布接口暴露外部配置使用独立的配置文件用于不同的上下文。 本文为读书笔记,对书中内容进行重点概括,并将书中的模块化结合微服务、Java9 Jigsaw谈谈理解。 本书概括 以Java软件系统为例,重点讲解了应用架构...

    seanHai 评论0 收藏0
  • Java9特性预览——Jshell

    摘要:什么是项目是第一个官方的的缩写,即交互式编程环境,是一种命令行工具。它允许你无需编写类或者方法来执行语句。它与的解释器或其它本地支持的语言,如和类似。在即将到来的的特性中,这绝对是更有趣的特性之一。 什么是JShell? JShell/Kulla项目是第一个官方的Java REPL (Read-Eval-Print-Loop的缩写,即交互式编程环境),是一种命令行工具。它允许你无需编写...

    hikui 评论0 收藏0
  • Java9的新特性

    摘要:新特性概述系列一安装及使用系列二运行系列三模块系统精要系列四更新系列五系列六系列七系列八系列九与的区别迁移注意事项参数迁移相关选项解析使用构建实例使用示例带你提前了解中的新特性 Java语言特性系列 Java5的新特性 Java6的新特性 Java7的新特性 Java8的新特性 Java9的新特性 Java10的新特性 Java11的新特性 Java12的新特性 Java13的新特性...

    ddongjian0000 评论0 收藏0
  • Oracle正式发布Java 9,引入新的Java编程组件

    摘要:的核心变化就是引入了一种新的编程组件,也就是模块,按照的说法,它是一个可命名的自描述的代码和数据集合。宣布会按照每六个月一次的节奏进行发布,意味着是最后一次特性驱动的版本发布,这反映出了目前管理的特点。 Java SE 9.0于2017年9月21日发布。JDK 9的核心变化就是引入了一种新的Java编程组件,也就是模块,按照Oracle的说法,它是一个可命名的、自描述的代码和数据集合。...

    shenhualong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<