模块设计的原则:
1、防止出现编译时循环依赖(主要是编译器不支持),但运行时是允许循环依赖的,比如GUI应用
2、明确模块的边界
几种模块设计:
API模块,聚合模块(比如java.base)
可选依赖
两种方式:
1、可选的编译时依赖(类似于maven的provided scope)声明: requires static , requires transitive static
2、使用services模式,缺点就是需要使用侵入性的ServiceLoader API
使用编译时可选依赖
module framework {
requires static fastjsonlib;
}
public static void main(String... args) {
try {
Class> clazz = Class.forName("javamodularity.fastjsonlib.FastJson");
FastJson instance =
(FastJson) clazz.getConstructor().newInstance();
System.out.println("Using FastJson");
} catch (ReflectiveOperationException e) {
System.out.println("Oops, we need a fallback!");
}
}
注意,通过requires static声明后,运行时,即使fastjsonlib模块在模块路径中,仍然会跑到异常块中,因为requies static声明的模块不会出现在模块解析路径上。除非你通过jlink打包时,加入--add-modules fastjsonlib选项来显式将其添加到模块解析路径(通过--add-modules也是作为一个root module).
使用Services模式的可选依赖
请参考之前的对于Services的探讨
Versioned Modules
jar命令打包时可以通过 --module-version=选项支持将版本添加到module-info.class中作为一个属性。但是对于模块解析而言,版本是没有意义的,模块解析过程中,只看模块名,不支持版本。
所以如果需要版本化,还是得借助于Maven,Gradle之类的打包工具。
资源封装
分模块内资源访问、模块间资源访问
模块内资源访问
firstresourcemodule/
├── javamodularity
│ └── firstresourcemodule
│ ├── ResourcesInModule.java
│ ├── ResourcesOtherModule.java
│ └── resource_in_package.txt 包内资源
├── module-info.java
└── top_level_resource.txt 与module-info.java平级的资源
访问方式有几种,见下面代码:
public class ResourcesInModule {
public static void main(String... args) throws Exception {
Class clazz = ResourcesInModule.class;
InputStream cz_pkg = clazz.getResourceAsStream("resource_in_package.txt"); //<1>
URL cz_tl = clazz.getResource("/top_level_resource.txt"); //<2>
Module m = clazz.getModule(); //<3>
InputStream m_pkg = m.getResourceAsStream(
"javamodularity/firstresourcemodule/resource_in_package.txt"); //<4>
InputStream m_tl = m.getResourceAsStream("top_level_resource.txt"); //<5>
assert Stream.of(cz_pkg, cz_tl, m_pkg, m_tl)
.noneMatch(Objects::isNull);
}
}
在模块化中,不推荐使用ClassLoder::getResource*
注意上面代码中用到了Module API
跨模块资源访问
.
├── firstresourcemodule
│ ├── javamodularity
│ │ └── firstresourcemodule
│ │ ├── ResourcesInModule.java
│ │ ├── ResourcesOtherModule.java
│ │ └── resource_in_package.txt
│ ├── module-info.java
│ └── top_level_resource.txt
└── secondresourcemodule
├── META-INF
│ └── resource_in_metainf.txt
├── foo
│ └── foo.txt
├── javamodularity
│ └── secondresourcemodule
│ ├── A.java
│ └── resource_in_package2.txt
├── module-info.java
└── top_level_resource2.txt
注意,下面代码的前提是两个模块的包都没暴露给对方
public class ResourcesOtherModule {
public static void main(String... args) throws Exception {
Optional otherModule = ModuleLayer.boot().findModule("secondresourcemodule"); //<1>
otherModule.ifPresent(other -> {
try {
InputStream m_tl = other.get