参考网站:The State of the Module System,Java9模块系统的说明(翻译),Java9 中的 Module, ModulePath 和 ClassPath,Java 9 揭秘(4. 模块依赖)
exports
中,java.lang
和java.lang.annotation
是不同的包,需要分开写,只写java.lang
则只能公开该包下的类,而不会公开java.lang.annotation下的类。使用示例(以及Maven整合java9):https://www.jianshu.com/p/66c26dd3237f
module {
requires ;
requires public ;
exports ;
}
exports packagea
。requires A
(同时还需要将该组件加入到项目->buildpath->Modulepath)requires B
,也无法使用A模块中的内容。如果希望让C模块可以通过引用模块B,从而间接引用A模块,则需要在B模块中声明requires public A
,这称为隐式可读性(implied readability)。module {
requires static ;
}
模块系统在编译时以及运行时验证模块的依赖关系。 有时希望在编译时模块依赖性是必需的,但在运行时是可选的。
你在开发一个库时,如果一个特定的模块在运行时可执行更好的库。否则,它将回到另一个模块,使其执行不到最佳的库。但是,库是根据可选模块进行编译的,如果可选模块不可用,则确保不依赖于可选模块的代码执行。
另一个例子是导出注解包的模块。 Java运行时已经忽略不存在的注解类型。 如果程序中使用的注释在运行时不存在,则注解将被忽略。 模块依赖关系在启动时验证,如果模块丢失,应用程序将无法启动。 因此,必须将含有注解包的模块的模块依赖性声明为可选。
以下模块声明包含对com.jdojo.annotation模块的可选依赖关系:
module com.jdojo.claim {
requires static com.jdojo.annotation;
}
允许在require语句中同时使用transitive 和static 修饰符:
module com.jdojo.claim {
requires transitive static com.jdojo.annotation;
}
如果transitive 和static 修饰符一起使用,则可以按任何顺序使用。 以下声明具有与之前相同的语义:
module com.jdojo.claim {
requires static transitive com.jdojo.annotation;
}
module A {
exports com.packageA
exports com.packageB to B;
exports com.packageC to C,D,E;
}
要反射公共字段,需要exports导出包,这是允许的最小可访问性。要反射非公共字段,必须打开opens该包,这是允许的最大可访问性。
open module com.jdojo.model {
// Module statements go here
}
打开一个包意味着允许其他模块对该包中的类型使用深层反射。 可以打开一个包指定给所有其他模块或特定的模块列表。
打开一个包到所有其他模块的打开语句的语法如下:
opens ;
这里,可用于深入反射所有其他模块。 也可以使用限定的open语句打开包到特定模块:
opens to , ...;
在这里,仅用于深层反射到,等。以下是在模块声明中使用opens语句的示例:
module com.jdojo.model {
// Export the com.jdojo.util package to all modules
exports com.jdojo.util;
// Open the com.jdojo.util package to all modules
opens com.jdojo.util;
// Open the com.jdojo.model.policy package only to the hibernate.core module
opens com.jdojo.model.policy to hibernate.core;
}
com.jdojo.model
模块导出com.jdojo.util
包,这意味着所有公共类型及其公共成员在编译时可以访问,并在运行时进行反射。com.jdojo.util
包的所有公共类型及其公共成员都可以在编译时访问,并且该包允许在运行时深层反射。com.jdojo.model.policy
包打包到hibernate.core
模块进行深层反射,这意味着其他模块在编译时不能访问此包的任何类型,而hibernate.core
模块可以访问所有类型及其成员在运行时进行深度反射。module com.demo.consumer {
requires com.example.data;
uses com.example.data.SortService;
}
uses
用于声明需要的接口,这样就可以使用ServiceLoader.load方法去加载依赖中的service provideruses [interface]
当前模块就会发现它,使java.util.ServiceLoader类进行加载。必须是本模块中的,不能是其他模块中的。其实现类可以由其他模块提供。module service.sort.bubble {
requires service.sort;
provides com.example.data.SortService with sort.impl.bubble.BubbleSort;
}
provides [interface] with [implement]
声明了是SortService的服务提供方,好让模块系统知道这个模块提供了该接口的实现。java1.8及以前的项目使用java9之后的模块化jar包:
java9之后,创建一个模块化的项目,但是依赖的Jar包还没有模块化:
requires public [com.automiaticModule]
,显示模块不需要考虑各个自动模块之间的相互依赖问题)Modulepath中的Jar包或Jmod文件被当作Module来处理,而Classpath中的的Jar包,无论是否模块化都会被当作传统Jar包处理。
modulepath中模块化jar包为显示模块,非模块化(没有模块声明)的jar包为自动模块;classpath中的jar包被视为未命名模块
未命名模块
Unnamed Module是一个特殊的,自动生成的Module,所有Classpath下的内容在 Java9中都是挂在Unnamed Module(未命名模块)名下的。对于同一个ClassLoader,只有一个 未命名模块。
Unnamed Module会导出它的所有包。
Unnamed Module可以读取其他任意模块。
显示模块不能读取未命名模块。(这个限制是有意为之的。因为允许命名模块依赖类路径中的任意内容是不可能做到可靠的配置的)
如果一个包被定义在了命名模块和未命名模块中,那么未命名模块中的包会被忽略。(假设com.xxxPackage
包是com.moduleA
模块的导出包,包里面有Alpha.class
类,同时如果一个类路径下的JAR文件包含com/xxxPackage/Alpha.class
类,则该文件将永远不会被加载。)
自动模块
一个不包含module-info.class的传统Jar包,如果放到了ModulePath下,就变成了一个自动模块(Automatic Module)。
自动模块默认的依赖于所有Modulepath中的模块,可以访问所有模块中导出的Package。默认导出此模块中的所有Package。
自动模块被授予对其它所有自动模块的隐式可读性*(*见总结1)。(自动模块之间)
如果 Jar 包在MetaInfo文件中定义了Automatic-Module-Name,则使用这个值作为模块的名称;如果没有定义,那么使用 Jar 包的文件名去掉扩展名的那部分作为模块名,其中包含的“-”要替换成“.”,因为模块名不允许包含“-”字符。
自动模块可以读取未命名模块。显示模块不能读取未命名模块。(例如,显示模块com.explicitModule
中的代码引用了自动模块com.automaticModule
中的公共类型,并且自动模块com.automaticModule
的方法签名引用了还在Classpath路径中的JAR文件中的类型,那么显示模块com.explicitModule
中的代码将无法访问这些类型,因为显示模块不能依赖未命名模块。临时的解决方案为:将显示模块com.explicitModule
改为自动模块,直到classpath中的jar包被模块化,并移入modulepath中)