JDK9的新特性:JPMS模块化

简介

JDK9引入了一个新的特性叫做JPMS(Java Platform Module System),也可以叫做Project Jigsaw。模块化的本质就是将一个大型的项目拆分成为一个一个的模块,每个模块都是独立的单元,并且不同的模块之间可以互相引用和调用。

在module中会有元数据来描述该模块的信息和该模块与其他模块之间的关系。这些模块组合起来,构成了最后的运行程序。

听起来是不是和gradle或者maven中的模块很像?

通过组件化,我们可以根据功能来区分具体的模块,从而保持模块内的高聚合,模块之间的低耦合。

另外,我们可以通过模块化来隐藏接口的具体实现内容,从而不影响模块之间的调用。

更多内容请访问www.flydean.com

最后,我们可以通过显示声明来描述模块之间的依赖关系。从而让开发者更好的理解系统的应用逻辑。

JDK9中模块的实现

在JDK9之前,java是通过不同的package和jar来做功能的区分和隔离的。

但在JDK9中,一切都发送了变化。

首先是JDK9本身的变化,JDK现在是以不同的模块来区分的,如果你展开IDE中JDK的依赖,会看到java.base,java.compiler等模块。

其中java.base模块比较特殊,它是独立的模块,这就意味着它并不依赖于其他任何模块,并且java.base是其他模块的基础,所以在其他模块中并不需要显式引用java.base。

我们再总结一下:

class是字段和方法的集合,package是class的集合,而module是package的集合。

一般来说使用模块和不使用模块对用户来说基本上是感觉不到的,因为你可以将模块的jar包当成普通的jar包来使用,也可以将普通的jar包当成模块的jar包来使用。

当使用普通的jar包时,JDK将会采用一种Automatic modules的策略将普通jar包当成module jar包来看待。

那么module和普通的jar包有什么区别呢?

  1. module中包含有一个module-info.class文件,这个文件定义了module本身的信息和跟外部module之间的关系。

  2. 要使用module jar包,需要将该jar包放入modulepath而不是classpath。

下面我们在具体的例子中详细探索一下module的使用。

JDK中的module

刚刚讲到了JDK也是以模块来区分的,并且所有模块的基础都是java.base。我们可以使用java --list-modules 来列出现有的所有module:

java --list-modules 

java.base@14.0.1
java.compiler@14.0.1
java.datatransfer@14.0.1
java.desktop@14.0.1
java.instrument@14.0.1
java.logging@14.0.1
java.management@14.0.1
java.management.rmi@14.0.1
....

也可以使用java --describe-module 来查看具体module的信息:

java --describe-module java.base

java.base@14.0.1
exports java.io
exports java.lang
exports java.lang.annotation
exports java.lang.constant
exports java.lang.invoke
exports java.lang.module
exports java.lang.ref
exports java.lang.reflect
exports java.lang.runtime
...

我们再具体看一下module-info.class文件中的内容:

module java.base {
exports java.io;
exports java.lang;
exports java.lang.annotation;
...

文件很长,具体就不一一列举了,有兴趣的朋友可以自行参阅。

看到了JDK自带的module,接下来我们创建几个自己的module来看看。

创建自己的module

假如我们有一个controller,一个service的接口,和两个service的实现。

为了简单起见,我们将这四个类分在不同的module中。

在IDEA中创建一个module很简单,只需要在java文件夹中添加module-info.java文件就可以了。如下图所示:

代码其实很简单,Controller引用了Service接口,而两个Service的实现又实现了Service接口。

看下controller和service两个模块的的module-info文件:

module com.flydean.controller {
requires com.flydean.service;
requires lombok;
}
module com.flydean.service {
exports com.flydean.service;
}

requires表示该模块所要用到的模块名字。exports表示该模块暴露的模块中的包名。如果模块不暴露出去,其他模块是无法引用这些包的。

看下在命令行怎么编译,打包和运行module:

$ javac
--module-path mods
-d classes/com.flydean.controller
${source-files}
$ jar --create
--file mods/com.flydean.controller.jar
--main-class com.flydean.controller.ModuleController.Main
-C classes/com.flydean.controller .
$ java
--module-path mods
--module com.flydean.controller

深入理解module-info

上一节我们将了module-info中的requires和exports。这一节我们来更多的讲解module-info中的其他用法。

transitive

先看下modulea的代码:

    public ModuleService getModuleService(){
return new ModuleServiceA();
}

getModuleService方法返回了一个ModuleService,这个ModuleService属于模块com.flydean.service,我们看下module-info的定义:

module com.flydean.servicea {
requires com.flydean.service;
exports com.flydean.servicea;
}

看起来好像没什么问题,但是如果有其他的模块来使用servicea的getModuleService方法就会有问题,因为该方法返回了模块com.flydean.service中定义的类。所以这里我们需要用到transitive。

module com.flydean.servicea {
requires transitive com.flydean.service;
exports com.flydean.servicea;
}

transitive意味着所有读取com.flydean.servicea的模块也可以读取 com.flydean.service。

static

有时候,我们在代码中使用到了某些类,那么编译的时候必须要包含这些类的jar包才能够编译通过。但是在运行的时候我们可能不会用到这些类,这样我们可以使用static来表示,该module是可选的。

比如下面的module-info:

module com.flydean.controller {
requires com.flydean.service;
requires static com.flydean.serviceb;
}

exports to

在module info中,如果我们只想将包export暴露给具体的某个或者某些模块,则可以使用exports to:

module com.flydean.service {
exports com.flydean.service to com.flydean.controller;
}

上面我们将com.flydean.service只暴露给了com.flydean.controller。

open pacakge

使用static我们可以在运行时屏蔽模块,而使用open我们可以将某些package编译时不可以,但是运行时可用。

module com.flydean.service {
opens com.flydean.service.subservice;
exports com.flydean.service to com.flydean.controller, com.flydean.servicea, com.flydean.serviceb;
}

上面的例子中com.flydean.service.subservice是在编译时不可用的,但是在运行时可用。一般来说在用到反射的情况下会需要这样的定义。

provides with

假如我们要在Controller中使用service的具体实现,比如servicea和serviceb。一种方法是我们直接在controller模块中使用servicea和serviceb,这样就高度耦合了。

在模块中,我们可以使用provides with来对模块之间的耦合进行解耦。

我们看下代码:

module com.flydean.controller {
uses com.flydean.service.ModuleService;
requires com.flydean.service;
requires lombok;
requires slf4j.api;
}
module com.flydean.servicea {
requires transitive com.flydean.service;
provides com.flydean.service.ModuleService with com.flydean.servicea.ModuleServiceA;
exports com.flydean.servicea;
}
module com.flydean.serviceb {
requires transitive com.flydean.service;
provides com.flydean.service.ModuleService with com.flydean.serviceb.ModuleServiceB;
exports com.flydean.serviceb;
}

在controller中,我们使用uses来暴露要实现的接口。而在具体的实现模块中使用provides with来暴露具体的实现。

怎么使用呢?我们在controller中:

public static void main(String[] args) {
List<ModuleService> moduleServices = ServiceLoader
.load(ModuleService.class).stream()
.map(ServiceLoader.Provider::get)
.collect(toList());
log.info("{}",moduleServices);
}

这里我们使用了ServiceLoader来加载接口的实现。这是一种很好的解耦方式,这样我可以将具体需要使用的模块放在modulepath中,实现动态的修改实现。

要想在maven环境中顺利完成编译,maven-compiler-plugin的版本必须要在3.8.1以上。

总结

本文介绍了JDK9中module的使用,并在具体的中介绍了更多的module-info的语法。

本文的例子https://github.com/ddean2009/

learn-java-base-9-to-20

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/jdk9-java-module-jpms/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

JDK9的新特性:JPMS模块化的更多相关文章

  1. Jdk14 都要出了,Jdk9 的新特性还不了解一下?

    Java 9 中最大的亮点是 Java 平台模块化的引入,以及模块化 JDK.但是 Java 9 还有很多其他新功能,这篇文字会将重点介绍开发人员特别感兴趣的几种功能. 这篇文章也是 Java 新特性 ...

  2. Java9 新特性 详解

    作者:木九天   <   Java9 新特性 详解  > Java9 新特性 详解 摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方 ...

  3. JDK 5 ~ 10 新特性倾情整理!

    JDK 5 ~ 10 新特性倾情整理! 最近连 JDK11都在准备发布的路上了,大家都整明白了吗?也许现在大部分人还在用6-8,8的新特性都没用熟,9刚出不久,10-11就不用说了. 为了大家对JDK ...

  4. Java 9的14个新特性总结

    Java 9 包含了丰富的特性集.虽然Java 9没有新的语言概念,但是有开发者感兴趣的新的API和诊断命令. 我们将快速的,着重的浏览其中的几个新特性:  模块化系统–Jigsaw 项目 模块化是一 ...

  5. JDK 5 ~ 11 新特性倾情整理

    为了大家对JDK有一个全面的了解,下面我为大家整理了JDK5~11的所有关键新特性! 先看一下JDK的版本迭代图: 注:   OpenJDK和JDK区别  GPL协议通用性公开许可证(General ...

  6. 最通俗易懂的 Java 11 新特性讲解

    大多数开发者还是沉浸在 Java 8 中,而 Java 14 将要在 2020 年 3 月 17 日发布了,而我还在写着 Java 11 的新特性.Java 11 是 Java 8 之后的第一个 LT ...

  7. JDK9 新特性

    JDK9 新特性目录导航 目录结构 模块化系统 jshell 多版本兼容JAR 接口的私有方法 改进try-with-resourcs 改进砖石操作符 限制使用单独下划线标识符 String存储结构变 ...

  8. 一文带你看遍 JDK9~14 的重要新特性!

    Java9 发布于 2017 年 9 月 21 日 .作为 Java8 之后 3 年半才发布的新版本,Java 9 带 来了很多重大的变化其中最重要的改动是 Java 平台模块系统的引入,其他还有诸如 ...

  9. JDK9新特性实战:流关闭新姿势

    做Java开发的都知道,每个资源的打开都需要对应的关闭操作,不然就会使资源一直占用而造成资源浪费,从而降低系统性能. 关于资源的关闭操作,从JDK7-JDK9有了不少的提升及简化. JDK6 在JDK ...

  10. web Servlet 3.0 新特性之web模块化编程,web-fragment.xml编写及打jar包

    web Servlet 3.0 模块化 原本一个web应用的任何配置都需要在web.xml中进行,因此会使得web.xml变得很混乱,而且灵活性差,因此Servlet 3.0可以将每个Servlet. ...

随机推荐

  1. EasyExcel使用及自定义设置单元格样式

    EasyExcel使用及自定义设置单元格样式 https://www.cnblogs.com/Hizy/p/11825886.html easyexcel 自动设置列宽 https://www.man ...

  2. 禁用Windows自动更新并允许手动更新

    新版的 Windows 经常会自动检查更新,然后在某个夜深人静的晚上帮你自动更新. 对于自动更新,一般的解决方案是直接禁用 Windows 更新服务.这种方式虽然关闭了自动更新,但会影响手动更新.Wi ...

  3. OpenCV开发笔记(七十五):相机标定矫正中使用remap重映射进行畸变矫正

    前言   相机标定,重映射可以进行插值映射从而矫正图像,这是一种方法,也有矩阵映射方法,本篇使用重映射方式解说畸变矫正的计算原理.   Demo   横向纵向区域固定拉伸:     横向纵向拉伸:   ...

  4. django中修改QueryDict数据类型和转成普通字典

    修改QueryDict的几种方式 简介 在正常的请求/响应周期中访问时,request.POST和request.GET上的QueryDict将是不可变的. 要获得可变版本,您需要使用QueryDic ...

  5. 对find命令结果进行操作

    # find匹配到一些文件后,可能希望对其进行一些操作,这时就可以使用-exec选项,exec选项后面跟着所要执行的命令,然后是一对{},一个空格和一个\,最后是一个分号; find . -type ...

  6. docker中container相关命令

    1.以tomcat镜像为例运行tomcat容器(运行tomcat实例) docker run tomcat 2.宿主机端口与容器端口进行映射 -p docker run -p 8080(系统上外部端口 ...

  7. c# 4.8 实现Windows 定时任务计划(Task Scheduler)

    分享一个我自己写的 Windows 定时任务计划(Task Scheduler) 动态创建代码,没做太多封装,留个实现笔记 首先封装一个简单配置项的类 1 public class TaskSched ...

  8. [Rust] 数据类型的转换

    数据类型的转换 类型转换的方式 Rust 提供了多种类型转换的方式. as T 用于数类型之间的转换.ixx, uxx, fxx 都可以. 注意:当溢出的时候,转换不会 panic,而是循环映射值. ...

  9. Anaconda与Python环境在Windows中的部署

      本文介绍在Win10电脑中,安装Anaconda环境与Python语言的方法.   在这里需要注意,本文介绍的方法是在电脑自身原本不含有Python的情况下进行的:如果大家电脑中原本就下载.安装过 ...

  10. C++ STL 容器 forward_list类型

    C++ STL 容器 forward_list类型 介绍 std::forward_list 是 C++ 标准模板库 (STL) 中的一个单向链表容器.与 std::list 不同,std::forw ...