转载

掌握Java9模块化系统-进阶部分

在上篇基础部分介绍了模块化系统的基本使用和使用命令编译和运行我们的模块。

再来看下模块相关的关键字:

掌握Java9模块化系统-进阶部分

目前为止我们熟悉的就是 exports/reuires/module ,其使用也是很简单的。下面将逐步介绍其他关键字的使用场景。

系统模块

Java9以后JDK本身也是模块化的,如果我们使用模块化机制那么引用系统的包和对象,也需要在模块定义中引入这些包的依赖模块。

掌握Java9模块化系统-进阶部分

其中java.base较为特殊。这个模块由编译自动添加到每个我们的自定义模块依赖声明中。我们看看JDK源码中是如何定义该模块的:

C:/Program Files/Java/jdk-10.0.1/lib/src.zip!/java.base/module-info.java

该模块导出了java.io/java.nio/java.lang/java.net/java.math/java.time/java.util等核心基础包,这样我们的代码中就不需要显示去导入这些包所在模块的依赖声明了。

遗留代码和匿名模块

由于Java9模块机制可以说是breaking change,所以他需要解决旧代码的兼容。如果我们的代码中不声明module-info.java,那么我们的代码在一个称为“unnamed module”模块中。

“unnamed module”有两个要的特性。首先,该模块里的所有包默认都会自动进行exports声明,因此其他外部模块对他的所有对象可读。其次,匿名模块能访问所有其他模块(当然是有权限的情况下)。

当我们代码不显示声明模块定义的时候,编译代码仍然使用的classpath机制而不是上文提到的mudule path机制。正是这两个缺省机制才能保证我们的项目从Java8及以下可以直接切换到Java10。而不需要对新的模块机制进行适配。

所以JDK的开发者在这方面还是付出了很多努力的,在此对他们表示深感钦佩。

导出到特定模块

如果你的模块中的某些对象只允许特定的模块使用,则可以使用 to 关键字:

exports packageName to moduleNames;

修改pojo模块定义:

module pojo {
    exports pojo to order;
}

我们再编译goods模块就会报错:

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

goods/src/goods/GoodsService.java:5: 错误: 程序包 pojo 不可见 import pojo.Goods; ^ (程序包 pojo 已在模块 pojo 中声明, 但该模块未将它导出到模块 goods) 1 个错误

可传递性transitive

上文的三个模块就是可传递性的栗子:goods->order->pojo,同时goods也依赖pojo。来看看目前goods模块的定义:

module goods{
    requires order;
    requires pojo;
}

显然requires pojo;和order模块中重复了,我们只需修改order的模块声明:

module order {
    exports order;
    requires transitive pojo;
}
module goods{
    requires order;
}

模块的开放

open module

一个模块里的包只有在模块中声明exports,别的模块才能进行访问。当然,我们要明白这里的访问指的的是静态编译期间的访问。其定义如下:

open module moduleName{
    //模块定义
}

下面举个栗子,我们在pojo模块新增一个包和类other.Other。这是一个空类啥都没有:

package other;

public class Other {
}然后

修改pojo模块定义,加上open关键字:

open module pojo {
    exports pojo;
}

我们在goods模块中使用:

GoodsService.java

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) throws Exception{
        String module = "test",exports = "test",with="with";
        System.out.println(new GoodsService().queryGoodsInfoByName("test"));

        //test open module
        Object other = Class.forName("other.Other");
        System.out.println(other);
    }
}

上边的栗子使用反射来创建Other的实例,运行程序发现确实可以。然而我们并没有在pojo模块定义:

exports other;

可见open关键字使得pojo的包和对象在运行期开放给了其他模块,然而在编译还是不开放的。修改示例代码:

Other other = new Other();

编译是不能通过的。

opens Statement

我们想更细粒度的开放包的访问性,可以使用opens把特定的包开放给外部模块,提供运行时访问权限。其使用如下:

opens packageName [to moduleName];

注意两点:1、opens声明不能用在open的module中,因为open module定义的模块所有包都有了open属性。2、opens只是开放了运行时访问权限,而不是编译期。3、opens可以指定具体的模块,只有指定的模块才能在运行时访问该包。

我们可以看到系统模块也是用这个机制,比如java.desktp:

opens com.sun.java.swing.plaf.windows to jdk.jconsole;
opens javax.swing.plaf.basic to jdk.jconsole;

requires static

使用static那么导入的模块只是在编译可用,在运行期是不可见的。使用该关键字则必须明确,引入的模块在运行期是可选的,否则用到就会报错。比如我们改变goods模块的定义:

module goods{
    requires static order;
}

然后编译,一切OK。但是在运行的时候就会报错:

Exception in thread "main" java.lang.NoClassDefFoundError: order/OrderService
	at goods/goods.GoodsService.<init>(GoodsService.java:9)
	at goods/goods.GoodsService.main(GoodsService.java:20)
Caused by: java.lang.ClassNotFoundException: order.OrderService
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
	... 2 more

Process finished with exit code 1

总结

模块的基本使用到此已经差不多了,我们学会了:

  • 如何定义模块
  • 系统模块和匿名模块
  • 模块依赖的可传递性
  • 如何将模块导出到目标模块
  • 运行时开放模块
  • 运行时开放指定模块下的包
  • 静态模块依赖

最后

后续会开篇讨论模块间的通信(通过ServiceProvider,使用provides with uses关键字),以及jlink等工具的初步使用。

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