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…...
打造专属功能生态:开源工具扩展系统全攻略
打造专属功能生态:开源工具扩展系统全攻略 【免费下载链接】TrafficMonitorPlugins 用于TrafficMonitor的插件 项目地址: https://gitcode.com/gh_mirrors/tr/TrafficMonitorPlugins 开源工具扩展系统是一套基于动态链接库(DLL)的功能…...
Qwen3-TTS-12Hz-1.7B-CustomVoice惊艳效果:葡萄牙语足球解说+俄语天气预报语音集
Qwen3-TTS-12Hz-1.7B-CustomVoice惊艳效果:葡萄牙语足球解说俄语天气预报语音集 1. 多语言语音合成的突破性进展 语音合成技术正在经历一场革命性的变革,而Qwen3-TTS-12Hz-1.7B-CustomVoice无疑是这场变革中的佼佼者。这个模型不仅在技术架构上实现了重…...
Qwen3字幕生成工具5分钟快速上手:零基础制作精准SRT字幕
Qwen3字幕生成工具5分钟快速上手:零基础制作精准SRT字幕 1. 工具简介与核心优势 1.1 什么是Qwen3字幕生成工具 Qwen3字幕生成工具是一款基于阿里云通义千问双模型的本地智能字幕生成解决方案。它由两个核心AI模型组成: Qwen3-ASR-1.7B:负…...
3分钟突破百度网盘资源壁垒:智能链接解析工具革新资源获取体验
3分钟突破百度网盘资源壁垒:智能链接解析工具革新资源获取体验 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否经历过这样的场景:导师分享的学术资料被提取码挡在门外,加班急需的项目…...
570-‘基于坠落机制改进的混沌麻雀算法SSACD‘在23个标准测试函数上可直接运行Matlab语言
570-基于坠落机制改进的混沌麻雀算法SSACD在23个标准测试函数测试可直接运行 Matlab语言 改进点如下: 1.首先,引入Sinusoidal混沌映射和变尺度混沌策略对种群进行初始化,提高种群多样性使算法具备跳出局部最优解的能力 2.其次,引入…...
Xilinx FPGA FIFO IP核复位机制深度解析与实战调试
1. Xilinx FPGA FIFO IP核复位机制基础解析 第一次接触Xilinx FPGA的FIFO IP核时,很多人都会在复位环节栽跟头。我刚开始用Vivado生成FIFO IP核时,就遇到过复位信号处理不当导致数据丢失的问题。FIFO(First In First Out)作为数据…...
STM32用KEIL调试总进不了main?可能是printf重定向惹的祸(附完整解决方案)
STM32调试卡在SystemInit?深入解析printf重定向与半主机模式陷阱 调试STM32时遇到程序卡在SystemInit函数而无法进入main函数的情况,往往会让开发者陷入长时间的排查困境。这种现象背后可能隐藏着多种原因,但其中最容易被忽视却又频繁出现的&…...
BepInEx:Unity游戏插件框架的模块化解决方案
BepInEx:Unity游戏插件框架的模块化解决方案 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx BepInEx是一款针对Unity游戏的插件框架,提供模块化的插件管理与…...
GraphRAG大揭秘:微软如何用知识图谱让AI问答更精准,效率翻倍!
微软推出的GraphRAG通过引入知识图谱技术,有效解决了传统RAG在信息连接和归纳总结上的不足。GraphRAG利用大模型构建知识图谱,实现实体和关系的结构化表示,显著提升答案的准确度与完整性,并支持多跳推理。文章详细介绍了知识图谱的…...
SurfaceView视觉优化实战:圆角与渐变蒙层的完美结合
1. SurfaceView视觉优化的核心价值 在Android开发中,SurfaceView因其独特的双缓冲机制和独立的绘图线程,成为视频播放、游戏渲染等高性能场景的首选组件。但原生SurfaceView的直角边框和单调的呈现方式,常常与现代化UI设计语言格格不入。我在…...
