初探Java 9 的的模块化

Java 9中最重要的功能,毫无疑问就是模块化(Module),它将自己长期依赖JRE的结构,转变成以Module为基础的组件,当然这在使用Java 9 开发也和以前有着很大的不同。

Java8或更加早期的系统的问题

  1. Jar文件,像rt.jar等jar文件太大的以至于不能使用在小设备和应用中。
  2. 因为JDK是太大的,我们的应用或设备不能支持更好的平台.
  3. 由于修饰符是public的缘故,每个人都可以通过此来进行访问,所以在当前Java系统的封闭性不是很强。
  4. 由于JDK,Jre过于庞大,以至于很难进行测试和维护应用。
  5. 由于public的关系,Java比较开放。不可避免的能访问象sun., .internal.*等的一些内部不重要的APIs。

Java9模块系统的特性

  1. 在Java SE 9中分离了JDK, JRE,jar等为更小的模。因此我们可以方便的使用任何我们想要的模块。因此缩减Java应用程序到小设备是非常容易的。
  2. 更加容易的测试和维护。
  3. 支持更好的平台。
  4. public不再仅仅是public。现在已经支持非常强的封闭性(不用担心,后边我们会用几个例子来解释)。
  5. 我们不能再访问内部非关键性APIs了。
  6. 模块可以非常安全地帮助我们掩藏那些我们不想暴露的内部细节,我们可以得到更好的Security。
  7. 应用会变的非常小,因为我们可以只使用我们要用的模块。
  8. 组件间的松耦合变得非常容易。
  9. 更容易支持唯一责任原则(SRP)。

目录结构

以前的jre中有一个很大的架包,jdk8 中rt.jar有62M,即便运行一个最简单的HelloWorld,都必须带上它。

jdk9 的目录

我们发现,jdk9 中没有Jre 文件,也没有rt.jar等这种很大的架包,但是它有了一个新的文件jmods,模块都是放在jmods文件夹中。

目前共有98个模块。

Module的相关属性

在主目录的/main/java/下新建module-info.java文件,可以管理这个项目的module。

module M {

}

模块命名

又称模块描述文件

模块命名需要保证单一,可以使用反向域名模式,如com.wuwii.xxx.xxx,这个模块会导出包com.wuwii

在JDK 9中, open, module, requires, transitive, exports, opens, to, uses, provides 和 with是受限关键字。只有当具体位置出现在模块声明中时,它们才具有特殊意义。 可以将它们用作程序中其他地方的标识符。

例如:可以在程序中声明一个module变量。

访问权限

模块之间的关系被称作readability(可读性),代表一个模块是否可以找到这个模块文件,并且读入系统中(注意:并非代表可以访问其中的类型)。在实际的代码,一个类型对于另外一个类型的调用,我们称之为可访问性(Accessible),这意味着可以使用这个类型; 可访问性的前提是可读性,换句话说,现有模块可读,然后再进一步检测可访问性(安全)。

导出语句将模块的指定包导出到所有模块或编译时和运行时的命名模块列表。 它的两种形式如下:

exports <package>;
exports <package> to <module1>, <module2>...;

以下是使用了导出语句的模块示例:

module java.xml.ws {
……
    exports com.oracle.webservices.internal.api.databinding to
        jdk.xml.ws;
    exports com.sun.xml.internal.ws.addressing to
        jdk.xml.ws,
        java.xml.bind;
……
}

开放语句允许对所有模块的反射访问指定的包或运行时指定的模块列表。 其他模块可以使用反射访问指定包中的所有类型以及这些类型的所有成员(私有和公共)。 开放语句采用以下形式:

opens <package>;
opens <package> to <module1>, <module2>...;

Tips

对比导出和打开语句。 导出语句允许仅在编译时和运行时访问指定包的公共API,而打开语句允许在运行时使用反射访问指定包中的所有类型的公共和私有成员。

module N {
    exports M;
    opens M;
}

阅读有关模块的时候会遇到三个短语:

  • 模块M导出包P
  • 模块M打开包Q
  • 模块M包含包R

前两个短语对应于模块中导出语句和开放语句。 第三个短语意味着该模块包含的包R既不导出也不开放。 在模块系统的早期设计中,第三种情况被称为“模块M隐藏包R”。

声明依赖关系

需要(require)语句声明当前模块与另一个模块的依赖关系。 一个名为M的模块中的“需要N”语句表示模块M取决于(或读取)模块N。语句有以下形式:

requires <module>;
requires transitive <module>;
requires static <module>;
requires transitive static <module>;
  • require语句中的静态修饰符表示在编译时的依赖是强制的,但在运行时是可选的。
  • requires static N语句意味着模块M取决于模块N,模块N必须在编译时出现才能编译模块M,而在运行时存在模块N是可选的。
  • require语句中的transitive修饰符会导致依赖于当前模块的其他模块具有隐式依赖性。
  • 假设有三个模块P,Q和R,假设模块Q包含requires transitive R语句,如果如果模块P包含包含requires Q语句,这意味着模块P隐含地取决于模块R。

配置服务

Java允许使用服务提供者和服务使用者分离的服务提供者机制。 JDK 9允许使用语句(uses statement)和提供语句(provides statement)实现其服务。

使用语句可以指定服务接口的名字,当前模块就会发现它,使用 java.util.ServiceLoader类进行加载。格式如下:

uses <service-interface>;

使用语句的实例如下:

module M {
    uses com.jdojo.prime.PrimeChecker;
}

com.jdojo.PrimeChecker是一个服务接口,其实现类将由其他模块提供。 模块M将使用java.util.ServiceLoader类来发现和加载此接口的实现。

提供语句指定服务接口的一个或多个服务提供程序实现类。 它采取以下形式:

provides <service-interface>
    with <service-impl-class1>, <service-impl-class2>...;

相同的模块可以提供服务实现,可以发现和加载服务。 模块还可以发现和加载一种服务,并为另一种服务提供实现。 以下是例子:

module P {
    uses com.jdojo.CsvParser;
    provides com.jdojo.CsvParser
        with com.jdojo.CsvParserImpl;
    provides com.jdojo.prime.PrimeChecker
        with com.jdojo.prime.generic.FasterPrimeChecker;
}

总结

需要注意的是,不只是jdk中内置的98种模块,引用maven的第三方架包,也需要module,

如用的比较多的日志

    /**
     * logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(LearnSoap.class);

配置

需要在module-info.java配置:

requires slf4j.api;

Java中的包已被用作类型的容器。 应用程序由放置在类路径上的几个JAR组成。 软件包作为类型的容器,不强制执行任何可访问性边界。 类型的可访问性内置在使用修饰符的类型声明中。 如果包中包含内部实现,则无法阻止程序的其他部分访问内部实现。 类路径机制在使用类型时线性搜索类型。 这导致在部署的JAR中缺少类型时,在运行时接收错误的另一个问题 —— 有时在部署应用程序后很长时间。 这些问题可以分为两种类型:封装和配置。

JDK 9引入了模块系统。 它提供了一种组织Java程序的方法。 它有两个主要目标:强大的封装和可靠的配置。 使用模块系统,应用程序由模块组成,这些模块被命名为代码和数据的集合。 模块通过其声明来控制模块的其他模块可以访问的部分。 访问另一个模块的部分的模块必须声明对第二个模块的依赖。 控制访问和声明依赖的是达成强封装的基础。 在应用程序启动时解决了一个模块的依赖关系。 在JDK 9中,如果一个模块依赖于另一个模块,并且运行应用程序时第二个模块丢失,则在启动时将会收到一个错误,而不是应用程序运行后的某个时间。 这是一个可靠的基础配置。

使用模块声明定义模块。 模块的源代码通常存储在名为module-info.java的文件中。 一个模块被编译成一个类文件,通常命名为module-info.class。 编译后的模块声明称为模块描述符。 模块声明不允许指定模块版本。 但诸如将模块打包到JAR中的jar工具的可以将模块版本添加到模块描述符中。

使用module关键字声明模块,后跟模块名称。 模块声明可以使用五种类型的模块语句:exports,opens,require,uses和provide。 导出语句将模块的指定包导出到所有模块或编译时和运行时的命名模块列表。 开放语句允许对所有模块的反射访问指定的包或运行时指定的模块列表, 其他模块可以使用反射访问指定包中的所有类型以及这些类型的所有成员(私有和公共)。 使用语句和提供模块语句用于配置模块以发现服务实现并提供特定服务接口的服务实现。

从JDK 9开始,open, module, requires, transitive, exports,opens,to,uses,provides和with都是受限关键字。 只有当具体位置出现在模块声明中时,它们才具有特殊意义。

参考文章

原文地址:https://www.cnblogs.com/qnight/p/8983152.html

时间: 2024-08-01 21:57:12

初探Java 9 的的模块化的相关文章

初探Java序列化(Serialization)

Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化Deserialization是一种将这些字节重建成一个对象的过程.[字节流的来回转换] Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端.这就需要有一种可以在两端传输数据的协议.Java序列化机制就是为了解决这个问题而产生. 将对象状态转换成字节流之后,可以用java.io包中各种字节流的类将其保存到文件中,管道到另一线程中或通过网络连接将对象数据发送到另一主机.对象序

初探Java序列化(2)-writeObject/readObject

上一篇<初探Java序列化(Serialization)>给我们大体介绍了什么是序列化和反序列化,以及解析了一下序列化出来的文件.接着我们看看JDK具体如何序列化一个Object. 在序列化过程中,虚拟机会试图调用对象类里的writeObject() 和readObject(),进行用户自定义的序列化和反序列化,如果没有则调用ObjectOutputStream.defaultWriteObject() 和ObjectInputStream.defaultReadObject().同样,在Ob

(读书笔记)Java应用架构设计-模块化模式与OSGi

本书主要模块化模式的好处.模块化方法与模式.OSGi简单使用等内容,分3大部分: 第一部分介绍了模块化概念,为什么要模块化,以及一些模块化要考虑的东西,如模块粒度,依赖关系,重用性灵活性等. 第二部分介绍模块化的一些模式,采用了GoF设计模式的格式(模式名称.模式表述.图示.描述.多种实现.效果.样例.小结),看着有些乱,但是收获不少. 第三部分介绍OGSi结合Java如何使用,以及如何模块化现有系统.Java中无法直接模块化(Java SE模块化功能Jigsaw被推迟到了Jave SE 9),

(札记)Java应用架构设计-模块化模式与OSGi

本书主要模块化模式的优点.模块化方法与模式.OSGi简单使用等内容.分3大部分: 第一部分介绍了模块化概念.为什么要模块化,以及一些模块化要考虑的东西,如模块粒度,依赖关系,重用性灵活性等. 第二部分介绍模块化的一些模式.採用了GoF设计模式的格式(模式名称.模式表述.图示.描写叙述.多种实现.效果.例子.小结),看着有些乱,可是收获不少. 第三部分介绍OGSi结合Java怎样使用.以及怎样模块化现有系统.Java中无法直接模块化(Java SE模块化功能Jigsaw被推迟到了Jave SE 9

Web/Java Web项目如何模块化?没有正文,别点

事情是这样的,我们是一家小软件公司,两三年前做了几个Java Web项目,由于薪资原因,原主程都离开了. 由于公司不规范,也没有留下正规的开发文档,只有一个源程序在手里.后面的很多系统维护都很被动. 领导就觉得说,这样不好,你们做软件要实现模块化.标准化,即使有人离职,新来的人也能迅速上手维护系统. 初衷是好的,我也想这样,可是,这些已经开发完成的系统还能如何模块化标准化呢? 谁能告诉我? 自己想的几个方案: 1. 整理数据库表结构 2. 整理界面样式,截图留存 3. 整理复杂业务的逻辑和流程图

初探Java字符串

链接:http://mccxj.github.io/blog/20130615_java-string-constant-pool.html String印象 初学java,就已经有字符串是不可变的盖棺定论,解释通常是:它是final的. 不过,String是有字面量这一说法的,这是其他类型所没有的特性(除原生类型).另外,java中也有字符串常量池这个说法,用来存储字符串字面量,不是在堆上,而是在方法区里边存在的. 字面量和常量池初探 字符串对象内部是用字符数组存储的,那么看下面的例子: St

java springmvc +spring+ mybaits 模块化开发框架 HTML5+css3.0+bootstrap响应式开发界面

需要源码,请加QQ:858-048-581 系统模块 1.   权限管理:点开二级菜单进入三级菜单显示 角色(基础权限)和按钮权限 角色(基础权限): 分角色组和角色,独立分配菜单权限和增删改查权限. 按钮权限: 给角色分配按钮权限. 2.   按钮管理:自定义按钮管理,维护按钮权限标识等 3.   菜单管理:无限级别自定义菜单,自定义菜单图标,业务菜单和系统菜单分离,菜单状态显示隐藏(递归处理) 4.   数据字典:无限级别,支持多级别无限分类.内设编号,排序等 5.   组织机构:无限级别,

初探Java多线程

多线程是由Java提出的概念,那么什么是线程呢?这里会涉及到几个名字听着很类似的东西:程序.线程.进程. 程序:存储在磁盘上的一系列的文件,包括可执行文件和不可执行文件. 进程:在内存中,每一个程序都会开启一个进程. 线程:线程是进程的最小执行单元,线程在寄存器中,每一个线程需要消耗一定的cpu资源和512k到1M的内存资源. 多线程:也就是同一个程序中开启多个线程就是多线程. 使用多线程有什么样的优势呢?我们都知道在Java中,代码是一句一句地往下执行的,上面的代码没有执行完毕,下面的另一段代

初探JAVA中I/O流(二)

1.缓冲输入文件 FileReader BufferedReader FileReader可以直接对文件进行读操作.但是简化编程,加快读取速度,我们加入了缓冲机制,使用了BufferedReader.BufferedReader内置了一个char[]数组(大小为8192)作为缓冲区,每次调用fill()函数将该缓冲区尽可能填满.而我们自己的程序在调用BufferedReader提供的方法时,实质上是从该缓冲区读取的. BufferedReader中的fill()方法 1 private void