转载

掌握Java9模块化系统-基础部分

模块基础

module是用来组织packages,通过模块名称(module’s name)来隔离不同模块之间包的可读性。因此是在Java的访问限制机制public/private/protected上加了一层,先有模块的可读性才有访问性可言。同时jlink工具提供了提取程序最小运行时的支持。

模块化机制给Java语言带来一些新的关键字如下:

掌握Java9模块化系统-基础部分

聪明的你可能会想起:在一个成熟的系统引入新的关键字,会不会对已有代码命名造成冲突。答案是:不会。因为这些关键字的使用限定在模块定义文件module-info.java,你在正常的Java代码中还是可以使用这些关键字作为标示符。

下面先稍微简单介绍下上面关键字的作用:

exportsrequires 是一对儿,前者用于导出模块的某些包,后者则是导入需要引入的外部模块的包。

provides / uses / with 则是服务提供者机制下的用到的关键字,这个后文有讲解。

transitive 顾名思义就是指的可传递性,主要用于多个模块串行依赖用于简化书写requires语句的。

to 主要是和 exports 组合,用于将模块的包导出到特定模块,成为其专属女佣。

open / opens 主要是用于将模块开放给其他所有模块,但是要注意编译期和运行期的区别。其具体机制后门也会详述。

好了,模块的基本概念就到这里了。聪明的你更期望的是一个能运行的实例吧,那我们下面就开始从代码的角度去使用我们的模块化机制咯。

简单的模块示例

下面我们将定义三个模块,这里还是沿用商品和订单的栗子。我们定义的三个模块组织如下:

掌握Java9模块化系统-基础部分

这里我们定义三个模块:goods/order/pojo,其依赖关系如下:

goods->order->pojo

接下来我们依次建立好代码目录和 Java文件。当然你也可以手动创建这些目录和代码。

  • pojo模块:

我们先定义两个实体类Goods.java和Order.java:

package pojo;

import java.util.List;

public class Goods {
    private String goodsName;
    private double price;
    private List<Order> orderList;

    public String getGoodsName() {
        return goodsName;
    }

    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public List<Order> getOrderList() {
        return orderList;
    }

    public void setOrderList(List<Order> orderList) {
        this.orderList = orderList;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "goodsName='" + goodsName + '/'' +
                ", orderList=" + orderList +
                '}';
    }
}

package pojo;

import java.time.LocalDateTime;

public class Order {
    private LocalDateTime createTime;
    private LocalDateTime finishTime;
    private String orderName;
    private String orderUser;

    public Order(String orderName) {
        this.orderName = orderName;
        this.createTime = LocalDateTime.now();
        this.finishTime = LocalDateTime.now().plusDays(2);
        this.orderUser = "gxf";
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    public LocalDateTime getFinishTime() {
        return finishTime;
    }

    public void setFinishTime(LocalDateTime finishTime) {
        this.finishTime = finishTime;
    }

    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    public String getOrderUser() {
        return orderUser;
    }

    public void setOrderUser(String orderUser) {
        this.orderUser = orderUser;
    }

    @Override
    public String toString() {
        return "Order{" +
                "createTime=" + createTime +
                ", finishTime=" + finishTime +
                ", orderUser='" + orderUser + '/'' +
                '}';
    }
}

接下来在模块的根目录创建module-info.java(注意模块声明文件必须在模块源代码文件的根目录):

module pojo {
    exports pojo;
}

至此我们定义了名为pojo的模块,并且导出pojo这个包以供其他模块使用。

  • order模块:

这里我们定义一个OrderService类提供商品订单查询方法,其代码如下:

package order;

import pojo.Order;

import java.util.List;

public class OrderService {
    public List<Order> queryOrdersByGoodsName(String goodsName) {
        return List.of(new Order("oder1"),new Order("oder2")
                ,new Order("oder3"),new Order("oder4"),new Order("oder5"));
    }
}

queryOrdersByGoodsName方法构造订单对象列表,注意这里使用Java10集合新的工厂方法来生成的List。

order模块依赖pojo模块,因此其module-info.java定义如下:

module order{
    exports order;
    requires pojo;
}

我们看到该模块依赖pojo同时导出order包,以为下面的goods模块依赖order。

  • goods模块

该模块定义GoodsService类,用于查询商品信息,其代码如下:

package goods;


import order.OrderService;
import pojo.Goods;

public class GoodsService {

    private OrderService orderService = new OrderService();

    Goods queryGoodsInfoByName(String goodsName) {
        Goods goods = new Goods();
        goods.setGoodsName(goodsName);
        goods.setOrderList(orderService.queryOrdersByGoodsName(goodsName));
        return goods;
    }

    public static void main(String[] args) {
        String module = "test",exports = "test",with="with";
        System.out.println(new GoodsService().queryGoodsInfoByName("test"));
    }
}

接下来也要定义其模块声明:

module goods{
    requires order;
    requires pojo;
}

因为暂时没有其他模块依赖goods,所以模块定义里没有声明其导出的包。

至此我们的三个模块已经建立完成。

编译和运行模块

编译和运行您可以借助IDE工具,但是为了更透彻的理解模块的定义,我们先采用javac命令来编译和运行我们的模块。

按照模块的依赖顺序,我们依次编译pojo->order->goods模块。

  • 编译pojo模块

我们使用javac进行编译,编译命令如下:

javac -verbose --module-path target -d target/pojo pojo/src/*.java pojo/src/pojo/*.java

--module-path 指定本次编译依赖的外部模块的搜索路径,因为pojo模块没有外部依赖,这个参数在此其实可以忽略。

-d 指定编译目标文件存放的目录

-verbose 打印详细日志便于我们观察编译的具体过程

执行此命令控制台打印信息系如下:

[语法分析开始时间 SimpleFileObject[D:/code/mods/pojo/src/module-info.java]] [语法分析已完成, 用时 34 毫秒] [语法分析开始时间 SimpleFileObject[D:/code/mods/pojo/src/pojo/Goods.java]] [语法分析已完成, 用时 11 毫秒] [语法分析开始时间 SimpleFileObject[D:/code/mods/pojo/src/pojo/Order.java]] [语法分析已完成, 用时 3 毫秒] [正在加载/modules/java.base/module-info.class] [正在加载/modules/java.base/java/util/List.class] [正在加载/modules/java.base/java/lang/Object.class] [正在加载/modules/java.base/java/lang/String.class] [正在加载/modules/java.base/java/time/LocalDateTime.class] [正在加载/modules/java.base/java/lang/Deprecated.class] [正在加载/modules/java.base/java/lang/Override.class] [正在加载/modules/java.base/java/lang/annotation/Annotation.class] [正在加载/modules/java.base/java/lang/annotation/Retention.class] [正在加载/modules/java.base/java/lang/annotation/RetentionPolicy.class] [正在加载/modules/java.base/java/lang/annotation/Target.class] [正在加载/modules/java.base/java/lang/annotation/ElementType.class] [正在检查<匿名>] [已写入DirectoryFileObject[target/pojo:module-info.class]] [正在检查pojo.Goods] [正在加载/modules/java.base/java/io/Serializable.class] [正在加载/modules/java.base/java/lang/AutoCloseable.class] [正在加载/modules/java.base/java/lang/Byte.class] [正在加载/modules/java.base/java/lang/Character.class] [正在加载/modules/java.base/java/lang/Short.class] [正在加载/modules/java.base/java/lang/Long.class] [正在加载/modules/java.base/java/lang/Float.class] [正在加载/modules/java.base/java/lang/Integer.class] [正在加载/modules/java.base/java/lang/Double.class] [正在加载/modules/java.base/java/lang/Boolean.class] D:/code/mods>javac -verbose --module-path target -d target/pojo pojo/src/*.java pojo/src/pojo/*.java [语法分析开始时间 SimpleFileObject[D:/code/mods/pojo/src/module-info.java]] [语法分析已完成, 用时 23 毫秒] [语法分析开始时间 SimpleFileObject[D:/code/mods/pojo/src/pojo/Goods.java]] [语法分析已完成, 用时 9 毫秒] [语法分析开始时间 SimpleFileObject[D:/code/mods/pojo/src/pojo/Order.java]] [语法分析已完成, 用时 2 毫秒] [正在加载/modules/java.base/module-info.class] [正在加载/modules/java.base/java/util/List.class] [正在加载/modules/java.base/java/lang/Object.class] [正在加载/modules/java.base/java/lang/String.class] [正在加载/modules/java.base/java/time/LocalDateTime.class] [正在加载/modules/java.base/java/lang/Deprecated.class] [正在加载/modules/java.base/java/lang/Override.class] [正在加载/modules/java.base/java/lang/annotation/Annotation.class] [正在加载/modules/java.base/java/lang/annotation/Retention.class] [正在加载/modules/java.base/java/lang/annotation/RetentionPolicy.class] [正在加载/modules/java.base/java/lang/annotation/Target.class] [正在加载/modules/java.base/java/lang/annotation/ElementType.class] [正在检查<匿名>] [已写入DirectoryFileObject[target/pojo:module-info.class]] [正在检查pojo.Goods] [正在加载/modules/java.base/java/io/Serializable.class] [正在加载/modules/java.base/java/lang/AutoCloseable.class] [正在加载/modules/java.base/java/lang/Byte.class] [正在加载/modules/java.base/java/lang/Character.class] [正在加载/modules/java.base/java/lang/Short.class] [正在加载/modules/java.base/java/lang/Long.class] [正在加载/modules/java.base/java/lang/Float.class] [正在加载/modules/java.base/java/lang/Integer.class] [正在加载/modules/java.base/java/lang/Double.class] [正在加载/modules/java.base/java/lang/Boolean.class] [正在加载/modules/java.base/java/lang/Void.class] [正在加载/modules/java.base/java/lang/invoke/StringConcatFactory.class] [正在加载/modules/java.base/java/lang/invoke/MethodHandles.class] [正在加载/modules/java.base/java/lang/invoke/MethodHandles$Lookup.class] [正在加载/modules/java.base/java/lang/invoke/MethodType.class] [正在加载/modules/java.base/java/lang/invoke/CallSite.class] [已写入DirectoryFileObject[target/pojo:pojo/Goods.class]] [正在检查pojo.Order] [正在加载/modules/java.base/java/time/temporal/Temporal.class] [正在加载/modules/java.base/java/time/temporal/TemporalAccessor.class] [正在加载/modules/java.base/java/time/temporal/TemporalAdjuster.class] [正在加载/modules/java.base/java/time/chrono/ChronoLocalDateTime.class] [正在加载/modules/java.base/java/lang/Comparable.class] [已写入DirectoryFileObject[target/pojo:pojo/Order.class]] [共 485 毫秒]

上面的输出信息表明:虽然我们的pojo没有显示声明requires哪些模块,但是系统默认导入了java.base模块。也就是说我们定义的模块都隐士依赖java.base模块,这仅仅是为了导入常见的一些Java类库。其他输出信息读者可以自行查看。

  • 编译order模块

类似的执行下边命令即可:

D:/code/mods>javac -verbose --module-path target -d target/order order/src/*.java order/src/order/*.java

[语法分析开始时间 SimpleFileObject[D:/code/mods/order/src/module-info.java]] [语法分析已完成, 用时 34 毫秒] [语法分析开始时间 SimpleFileObject[D:/code/mods/order/src/order/OrderService.java]] [语法分析已完成, 用时 2 毫秒] [正在加载target/pojo/module-info.class] [正在加载/modules/java.base/module-info.class] [正在加载target/pojo/pojo/Order.class] [正在加载/modules/java.base/java/util/List.class] [正在加载/modules/java.base/java/lang/Object.class] [正在加载/modules/java.base/java/lang/String.class] [正在加载/modules/java.base/java/lang/Deprecated.class] [正在加载/modules/java.base/java/lang/annotation/Retention.class] [正在加载/modules/java.base/java/lang/annotation/RetentionPolicy.class] [正在加载/modules/java.base/java/lang/annotation/Target.class] [正在加载/modules/java.base/java/lang/annotation/ElementType.class] [正在检查<匿名>] [已写入DirectoryFileObject[target/order:module-info.class]] [正在检查order.OrderService] [正在加载/modules/java.base/java/io/Serializable.class] [正在加载/modules/java.base/java/lang/AutoCloseable.class] [正在加载/modules/java.base/java/util/Collection.class] [正在加载/modules/java.base/java/lang/Iterable.class] [已写入DirectoryFileObject[target/order:order/OrderService.class]] [共 684 毫秒]

输出信息我们可以清楚的看到,该模块先加载target/pojo/module-info.class模块然后加载java.base模块。

  • 编译goods模块

D:/code/mods>javac -verbose --module-path target -d target/goods goods/src/*.java goods/src/goods/*.java

[语法分析开始时间 SimpleFileObject[D:/code/mods/goods/src/module-info.java]] [语法分析已完成, 用时 32 毫秒] [语法分析开始时间 SimpleFileObject[D:/code/mods/goods/src/goods/GoodsService.java]] [语法分析已完成, 用时 7 毫秒] [正在加载target/order/module-info.class] [正在加载target/pojo/module-info.class] [正在加载/modules/java.base/module-info.class] [正在加载target/order/order/OrderService.class] [正在加载target/pojo/pojo/Goods.class] [正在加载/modules/java.base/java/lang/Object.class] [正在加载/modules/java.base/java/lang/String.class] [正在加载/modules/java.base/java/lang/Deprecated.class] [正在加载/modules/java.base/java/lang/annotation/Retention.class] [正在加载/modules/java.base/java/lang/annotation/RetentionPolicy.class] [正在加载/modules/java.base/java/lang/annotation/Target.class] [正在加载/modules/java.base/java/lang/annotation/ElementType.class] [正在检查<匿名>] [已写入DirectoryFileObject[target/goods:module-info.class]] [正在检查goods.GoodsService] [正在加载/modules/java.base/java/io/Serializable.class] [正在加载/modules/java.base/java/lang/AutoCloseable.class] [正在加载/modules/java.base/java/util/List.class] [正在加载target/pojo/pojo/Order.class] [正在加载/modules/java.base/java/lang/System.class] [正在加载/modules/java.base/java/io/PrintStream.class] [正在加载/modules/java.base/java/lang/Appendable.class] [正在加载/modules/java.base/java/io/Closeable.class] [正在加载/modules/java.base/java/io/FilterOutputStream.class] [正在加载/modules/java.base/java/io/OutputStream.class] [正在加载/modules/java.base/java/io/Flushable.class] [已写入DirectoryFileObject[target/goods:goods/GoodsService.class]] [共 650 毫秒]

至此我们完成了所有模块的编译,现在我们来看看编译结果:

掌握Java9模块化系统-基础部分

  • 运行

我们的运行入口在goods.GoodsService,运行命令如下:

D:/code/mods>java --module-path target -m goods/goods.GoodsService

Goods{goodsName='test', orderList=[Order{createTime=2018-06-25T14:56:53.541093100, finishTime=2018-06-27T14:56:53.541093100, orderUser='gxf'}, Order{createTime= 2018-06-25T14:56:53.541093100, finishTime=2018-06-27T14:56:53.541093100, orderUser='gxf'}, Order{createTime=2018-06-25T14:56:53.541093100, finishTime=2018-06-27 T14:56:53.542053500, orderUser='gxf'}, Order{createTime=2018-06-25T14:56:53.542053500, finishTime=2018-06-27T14:56:53.542053500, orderUser='gxf'}, Order{createT ime=2018-06-25T14:56:53.542053500, finishTime=2018-06-27T14:56:53.542053500, orderUser='gxf'}]}

--module-path 指定模块根目录,也就是我们编译结果存放的根目录

-m 指定程序的入口,其用法是:

-m <module>/<main-class>

因此必须是带模块名然后是主类名。

好了,至此模块的基础知识我们已经掌握的差不多了。如果你不喜欢命令行,则可以考虑使用idea来进行编译。在idea编译的时候会报错找不到模块,根据提示将依赖模块导入即可。

掌握Java9模块化系统-基础部分

最后关于代码下载

因为代码量很少,建议大家还是手动创建一遍,毕竟代码都可以复制粘贴了。自己实践一遍是不是印象更深刻呢。

由于篇幅和时间关系,关于模块的其他知识我会另起一篇再进行讲述,希望大家每天都有进步。

原文  https://juejin.im/post/5b305bf451882574b55e5ee1
正文到此结束
Loading...