Java 9 模块化系统详解
Java 9 模块化系统详解
- 一、简介
- 1. 引入模块化系统原因
- 2. 模块化系统带来的优势和挑战
- 3. 模块化关键概念
- 二、模块化基础
- 1. 模块化源代码结构规范
- 2. 模块定义与描述符
- 3. 打包可执行模块
- 三、模块化系统的高级特性
- 1. 模块发现与解决依赖
- 2. 模块化升级与替换
- 3. 模块化动态访问
- 四、Java模块化实践
- 如何创建模块
- 步骤1:创建module-info.java文件
- 步骤2:将项目转换成一个模块
- 步骤3:运行程序
- 如何使用其他模块
- 如何向后兼容
- 五、模块化与类路径
- 1. 模块化系统与传统类路径系统的区别
- 2. 模块路径与类路径的混合使用
- 六、模块化系统尚未解决的问题以及对于代码库的选项
- 1. Automatic Module Name
- 2. JBoss Modules 和 OSGi
- 3. 微服务下的模块化
- 七、模块化系统的迁移指南和最佳实践
- 1. 迁移指南
- 2. 实践经验
一、简介
1. 引入模块化系统原因
Java在发展过程中逐渐变得庞大而复杂,Java应用程序的规模逐渐变大,尤其是企业级应用代码结构臃肿不断添加自定义库或第三方依赖,这样会导致潜在的问题,例如包访问冲突和兼容性影响等。因此Java SE 9 引入了一种新的模块化系统来解决这些问题。
2. 模块化系统带来的优势和挑战
模块化可以更好地控制Java项目中使用的库,将一个完整的应用程序分解成合理的模块,可以消除在代码方面缺乏结构和组织性的问题,使编码更加模块化、灵活和可维护。另一方面模块化系统也会带来一些挑战,例如要花费额外的精力来处理模块之间的依赖关系以及必须要重新设计和组织项目结构中的源代码。
3. 模块化关键概念
-
模块(Module):Java 模块是一个可以独立编译和部署的程序单元。模块是由包(Package)组成的,包含了该模块提供给外部的 API,也包含了该模块内部实现所需要的代码和数据。每个模块带有一个模块描述符,指定了模块的名称、版本、输入和输出。
-
模块路径(Module Path):模块路径用于识别模块的引用,就像类路径用于识别类的引用一样。通过将模块添加到模块路径中,Java 运行时可以找到它们并使它们可用。
-
需求(Requirement):需求是指一个模块依赖于另一个模块。模块描述符中定义了模块的需求列表,模块对其他模块的依赖是使用 require 语句进行声明的。
-
导出(Export):导出指明了模块提供的 API 包,其他模块可以使用这些 API。如果一个模块没有声明 API(即没有导出),则其他模块无法使用该模块的 API。
-
打包(Packaging):打包是指将模块的源代码或编译后的字节码存储成一个模块文件,以便部署。
二、模块化基础
1. 模块化源代码结构规范
Java SE 9 明确了使用模块的源代码结构规定,相比传统的Java应用程序它们的代码结构变得更加整洁。Java 的模块化源代码结构规范具体如下:
src/<module name>/module-info.javapackage1/...package2/...
模块源代码存储在名为“src”目录下,每个模块有自己的目录,在这个目录下每个文件包含了特定的软件组件。其中使用 module_info.java 文件来代表此模块。
2. 模块定义与描述符
在 Java 模块中模块描述符表示了模块对其他模块的依赖、导出等信息,它是指定用于组合和部署模块化应用程序的元数据文件。有关模块中描述符的细节介绍可以参考下面的代码示例
模块定义与描述符示例:
module com.mycompany.mymodule {requires org.slf4j;exports com.mycompany.mymodule.api;
}
模块描述符由一系列语句和指令组成,以空格、制表符、换行符作为分隔符进行书写。以上面的示例代码为例说明以下重要概念:
-
module:定义模块。模块名称与文件系统上该模块所属的目录名称是一致的
-
requires:声明一个模块依赖org.slf4j 是其依赖的模块名称。
-
exports:公开包。导出的包对外公布的类、接口将可以在其他模块中使用,如果不明确导出在 modules 中是不能调用的。
3. 打包可执行模块
在 Java SE 9 中,每个模块需要先编译成一组字节码,然后将其打包到一个单独的 JAR 文件中。可以使用Java平台中提供的 jlink 命令将模块链接起来并创建自定义运行时映像,以此打包每个模块中的所有类和依赖。这里给出一个简单示例:
javac -d mods/com.mycompany.mymodule \src/com.mycompany.mymodule/module-info.java \src/com.mycompany.mymodule/com/mycompany/mymodule/MyClass.java
打包结果:
├── mods
│ └── com.mycompany.mymodule-1.0.jar
└── src└── com.mycompany.mymodule├── com│ └── mycompany│ └── mymodule│ └── MyClass.java└── module-info.java
三、模块化系统的高级特性
1. 模块发现与解决依赖
通过指定相应的 Requires 表达式,让别的模块去满足我们的模块要求所需要的依赖关系,Java SE 9 支持两种形式的依赖关系,分别为“强”与“传递”。
链接多个模块的集合被称为运行时图(Runtime图),运行时图的创建是通过实现模块之间的所有 Require 表达式来完成的。
2. 模块化升级与替换
Java SE9 提供了“模块可见性”这一高级特性,可以根据具体需求隐藏某些模块,防止在模块升级和替换时出现不必要的访问和引用。使用方式如下:
在 Java 配置文件 (java.conf) 中,修改模块路径为:
modulepath=/usr/java/jdk-9.0.1/conf/runtime-image
# hidden:
//ignored module notfound.module
# opened /opt/found/bin to foo.bar
在编译时添加 -Xmodule:module/name 参数
3. 模块化动态访问
JEP 261 提供了一种新的方式,使得从另一个模块中加载类变得尽可能简单。应用程序可以请求访问其它模块的非公共类、接口和枚举,而无需将这些包或类全部导出为 public API。这种新的方式支持从程序内部动态增加、删除和重定位模块。此外Java 9 引入了一系列API,以便在运行时获取关于模块化自身的元数据信息,如 package\ class,及其他模块的描述拉GTE信息等。
一个简单的动态访问模块的示例:
// 动态获取模块属性
Module module = Class.forName("com.example.MyClass").getModule();
System.out.println(module.getName());
四、Java模块化实践
如何创建模块
Java 9 引入了模块化系统使得我们可以更好地控制代码的可见性、隔离性和依赖性。下面是创建模块的详细步骤:
步骤1:创建module-info.java文件
在Java 9中可以使用 module-info.java 文件定义一个模块及其依赖关系。只需要在项目根目录下创建一个名为 module-info.java 的文件,并编写以下内容即可:
module com.example.myapp {requires java.base;exports com.example.mypackage;
}
module表示声明一个新的模块。com.example.myapp是该模块的名称。requires java.base表示该模块需要依赖于 Java 的基本模块。exports com.example.mypackage表示该模块对外暴露的包。如果有多个包需要对外暴露,可以使用逗号分隔。
步骤2:将项目转换成一个模块
要将现有项目转换为一个模块需要在编译时增加参数 --module-path 和 --module。例如:
javac --module-path mods/ -d out/ src/com/example/mypackage/MyClass.java src/module-info.java
--module-path指定模块路径,该路径应该是一个目录或 jar 文件。-d指定编译输出的目录。src/module-info.java是我们创建的 module-info.java 文件的路径。src/com/example/mypackage/MyClass.java是应用程序代码的路径。
步骤3:运行程序
要运行一个模块化的程序必须使用 --module 参数指定要运行的模块,例如:
java --module-path mods/ -m com.example.myapp/com.example.mypackage.MyClass
这条命令会运行 com.example.myapp 这个模块中的名为 com.example.mypackage.MyClass 的主类。
如何使用其他模块
在一个模块中使用其他模块非常简单,只需要在模块描述文件中声明这个模块即可。
- 声明依赖关系
在 module-info.java 文件中使用 requires 关键字声明依赖关系,例如:
module com.example.myapp {requires com.example.mymodule;
}
- 将外部模块添加到模块路径
使用 --module-path 参数将外部模块添加到模块路径中,例如:
java --module-path mods/ -m com.example.myapp/com.example.mypackage.MyClass
其中 mods/ 目录保存了所有需要使用的外部模块。如果需要加载多个模块,则可以使用冒号分隔每个模块的路径,例如:
java --module-path mods1/:mods2/ -m com.example.myapp/com.example.mypackage.MyClass
如何向后兼容
Java 模块化系统非常注重向后兼容。模块化应用程序可以像传统的 Java 应用程序一样运行在非模块化的环境中。
具体来说如果不提供 module-info.java 文件,则编译器会自动将我们的代码封装到一个自动模块中。自动模块的名称是基于 jar 文件的名称推断出来的,并且它依赖于所有其它的模块。
此外在使用传统的类路径方式运行模块化应用程序时,Java 运行时会将整个类路径看作单个模块。这个模块的名称是 unnamed module,并且它依赖于所有其他的模块。
五、模块化与类路径
1. 模块化系统与传统类路径系统的区别
- 模块化系统通过定义明确的依赖关系来提高可靠性和安全性,并使代码更易于维护。
- 传统类路径系统的依赖关系通常是隐式的,因此在大型应用程序中很容易出现混乱和冲突。
2. 模块路径与类路径的混合使用
当在同一个应用程序中混合使用模块路径和类路径时,应该注意以下两点:
- 类路径中的类无法访问模块路径中的类。如果需要在类路径中使用模块中的类,可以将模块导出到一个 jar 文件中,然后将该 jar 文件添加到类路径中。
- 在 Java 11 之前,classpath 参数被解释为类路径,但从 Java 11 开始,它被解释为模块路径。
六、模块化系统尚未解决的问题以及对于代码库的选项
1. Automatic Module Name
当我们在模块化应用程序中使用非模块化的库时,我们需要指定该库的模块名称,这就是 Automatic Module Name。
Automatic Module Name 是 Java 9 中引入的一个特性,它允许为非模块化的 Jar 包提供一个模块名称。
具体来说如果 jar 文件没有 module-info.class 文件,则它可以通过在 MANIFEST.MF 文件中添加一个名为 Automatic-Module-Name 的属性来设置模块名称。例如:
Automatic-Module-Name: mylibrary
这将使得我们可以像依赖于其他模块一样依赖于这个自动模块。
2. JBoss Modules 和 OSGi
虽然 Java 9 引入了官方的模块化系统,但 JBoss Modules 和 OSGi 这两种传统的模块化系统仍然在某些场合下非常有用。
JBoss Modules 是一个模块化系统,它是由 JBoss 作为其应用服务器的一部分开发的。与 Java 9 的模块化系统相比,JBoss Modules 更为灵活,允许多个版本的同一个库同时存在,并且支持动态加载和卸载模块。
OSGi 是一种更加通用的模块化系统,它可以在任何 Java 环境中使用。与 Java 9 的模块化系统相比,OSGi 具有更高的动态性和扩展性。
3. 微服务下的模块化
在微服务架构中,模块化非常重要。每个微服务都应该具有独立的、可重用的模块,并且这些模块应该很容易地被其他微服务使用。
Java 模块化系统为我们提供了一种良好的方式来组织和保护微服务代码库,使其更具可重用性和可维护性。如果您正在构建微服务应用程序,那么模块化应该是您的首选方案。
七、模块化系统的迁移指南和最佳实践
1. 迁移指南
要迁移到 Java 9 模块化系统,需要完成以下步骤:
- 确认项目是否具有明确定义的模块界限。
- 在所有 Java 源代码根目录下创建一个 module-info.java 文件。
- 在 module-info.java 文件中声明模块依赖关系。
- 更新构建脚本以在编译时使用新的命令行选项。
- 将应用程序的所有外部库转换为 Java 模块(可能需要创建自动模块名称)。
- 使用最新版本的 JDK 运行应用程序。
2. 实践经验
在实践中可以遵循以下最佳实践:
- 定义一个清晰的目录结构,每个模块都有自己的代码仓库、测试文件夹和文档。
- 将每个模块的界限设计为尽可能小而精细的单元。这样可以更容易地维护代码并提高可重用性。
- 尽量使用对外不可见的包。例如,在一个模块中只需将包导出给其他互相关联的模块。
- 尝试避免使用反射功能,因为这可能会破坏模块的可见性规则。
相关文章:
Java 9 模块化系统详解
Java 9 模块化系统详解 一、简介1. 引入模块化系统原因2. 模块化系统带来的优势和挑战3. 模块化关键概念 二、模块化基础1. 模块化源代码结构规范2. 模块定义与描述符3. 打包可执行模块 三、模块化系统的高级特性1. 模块发现与解决依赖2. 模块化升级与替换3. 模块化动态访问 四…...
Windows定时执行Python脚本
在Linux环境下我们可以使用crontab工具来定时的执行脚本,可以很轻松的管理各个虚拟环境下的py文件在Windows上可以使用任务计划程序来定时执行我们的脚本 关于这个的基本使用可以查看我前面的博客 https://blog.csdn.net/wyh1618/article/details/125725967?spm10…...
数据科学简介:如何使用 Pandas 库处理 CSV 文件
部分数据来源:ChatGPT 什么是 CSV 文件? CSV ( Comma Separated Values)文件是一种常见的文本文件格式,它通常用于存储结构化数据,因为它可以轻松地转换成电子表格,如Excel。 CSV 文件是以逗号作为分隔符的表格数据。文件中的每行代表一个记录,每列代表一个属性。例如…...
面试专题:java多线程(2)-- 线程池
1.为什么要用线程池? 线程池提供了一种限制和管理资源(包括执行一个任务)。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。 这里借用《Java并发编程的艺术》提到的来说一下使用线程池的好处: 降低资源消…...
Linux文件权限及用户管理
文件权限 在Linux中,每个文件和目录都有一组权限,这些权限决定了哪些用户可以访问文件或目录,以及他们可以进行什么样的操作。权限分为三类: 所有者权限:这些权限适用于文件或目录的所有者。 组权限:这些…...
以AI为灯,照亮医疗放射防护监管盲区
相信绝大部分人都有在医院拍X光片的经历,它能够让医生更方便快速地找出潜在问题,判断病人健康状况,是医疗诊断过程中的常见检查方式。但同时X射线也是一把双刃剑,它的照射量可在体内累积,对人体血液白细胞有杀伤力&…...
Golang单元测试详解(一):单元测试的基本使用方法
Golang 单元测试 Golang 中的单元测试是使用标准库 testing 来实现的,编写一个单元测试是很容易的: 创建测试文件:在 Go 项目的源代码目录下创建一个新的文件(和被测代码文件在同一个包),以 _test.go 为后…...
数据库的序列
目录 一、序列是什么 二、序列的用途 二、创建序列 三、查看、修改、删除序列 四、使用序列 (1)在插入语句中使用 (2)不在插入语句中使用 五、使用序列的例子 一、序列是什么 数据库对象分为:用户、视图、索引…...
2022年回顾
年总写完了(已持续多年),顺便写个小的回顾。 寻找属于自己的方向 无论当前干啥,大多数都不是真正适合你的,但是,你又不能不做下去,那么,持续的寻找适合的,就是一种解开…...
40亿个QQ号,限制1G内存,如何去重?
40亿个unsigned int,如果直接用内存存储的话,需要: 4*4000000000 /1024/1024/1024 14.9G ,考虑到其中有一些重复的话,那1G的空间也基本上是不够用的。 想要实现这个功能,可以借助位图。 使用位图的话&a…...
【django】django的orm的分组查询
前言:django当中分组查询如何实现? annotate from myapp import models from django.db.models.functions import TruncMonth from django.db.models import Count,Avg# 分组 values 就是取值作用 model.Book.objects.values(month).annotate(countCo…...
MySQL5.8在Windows下下载+安装+配置教程
MySQL是一款常用的关系型数据库管理系统,本文将介绍MySQL5.8在Windows下的安装配置教程。 1. 软件下载地址 免安装版下载地址:https://dev.mysql.com/downloads/mysql/安装版下载地址:https://dev.mysql.com/downloads/installer/ 2. 免安…...
Flask or FastAPI? Python服务端初体验
1. 引言 最近由于工作需要,又去了解了一下简单的python服务搭建的相关工作,主要是为了自己开发的模型或者工具给同组的人使用。之前介绍的针对于数据科学研究比较友好的一个可以展示的前端框架Streamlit可以说是一个利器。不过,随着ChatGPT的…...
《计算机组成原理》唐朔飞 第7章 指令系统 - 学习笔记
写在前面的话:此系列文章为笔者学习计算机组成原理时的个人笔记,分享出来与大家学习交流。使用教材为唐朔飞第3版,笔记目录大体与教材相同。 网课 计算机组成原理(哈工大刘宏伟)135讲(全)高清_…...
Linux:apache网页优化
Linux:apache网页优化 一、Apache 网页优化二、网页压缩2.1 检查是否安装 mod_deflate 模块2.2 如果没有安装mod_deflate 模块,重新编译安装 Apache 添加 mod_deflate 模块2.3 配置 mod_deflate 模块启用2.4 检查安装情况,启动服务2.5 测试 m…...
涨点技巧:注意力机制---Yolov8引入Resnet_CBAM,CBAM升级版
1.计算机视觉中的注意力机制 一般来说,注意力机制通常被分为以下基本四大类: 通道注意力 Channel Attention 空间注意力机制 Spatial Attention 时间注意力机制 Temporal Attention 分支注意力机制 Branch Attention 1.1.CBAM:通道注意力和空间注意力的集成者 轻量级…...
solr教程
一:安装配置 下载完成之后,解压solr文件,解压tomcat 1.1 在tomcat安装solr,并且建立solrCore 把solr5.5目录下的server/solr-webapp/webapp 重命名为solr,并且放置到tomcat/webapp的目录下。 打开tomcat/webapp/solr/WEB-INF/web.xml新建…...
基于java语言编写的爬虫程序
Java语言可以使用Jsoup、HttpClient等库进行网络爬虫开发,其中Jsoup提供了HTML解析和DOM操作的功能,HttpClient则提供了HTTP协议的支持。你可以通过使用这些库,构建网络爬虫程序来爬取指定网站的数据。需要注意的是,应该遵守网站的…...
UM2082F08 125k三通道低频无线唤醒ASK接收功能的SOC芯片 汽车PKE钥匙
1产品描述 UM2082F08是基于单周期8051内核的超低功耗8位、具有三通道低频无线唤醒ASK接收功能的SOC芯片。芯片可检测30KHz~300KHz范围的LF (低频)载波频率数据并触发唤醒信号,同时可以调节接收灵敏度,确保在各种应用环境下实现可靠唤醒,其拥…...
【SpringBoot_Project_Actual combat】 Summary of Project experience_需要考虑的问题
无论是初学者还是有经验的专业人士,在学习一门新的IT技术时,都需要采取一种系统性的学习方法。那么作为一名技术er,你是如何系统的学习it技术的呢。 一、DB Problems 数据库数据类型与java中数据类型对应问题? MySql数据库和java…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
统计学(第8版)——统计抽样学习笔记(考试用)
一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征(均值、比率、总量)控制抽样误差与非抽样误差 解决的核心问题 在成本约束下,用少量样本准确推断总体特征量化估计结果的可靠性(置…...
