当前位置: 首页 > 后端技术 > Java

Java9的模块(Module)系统

时间:2023-04-01 15:57:14 Java

Java的模块是Java9正式实现的,一直没时间研究这个东西。今天就和大家一起学习这个功能。Java模块解决了什么问题?最近有很多同学问我,胖哥,我应该怎么学?我应该学习什么?胖哥也穿插在这里。不管学什么,首先要搞清楚学习有什么用,是学了马上用还是以后有用。我觉得在时间有限的情况下,一定要学一些立马有用的东西。接下来,我们就来看看Java模块的用处吧。我觉得模块化最大的意义就是把代码逻辑按照功能分离出来,就像你做前端,我写后端,他做测试,把整体的大概念拆分成小概念,自由自在使用时结合它们,按需引用它们。其实它确实有这方面的作用,但不仅仅是那么多。简化类库JDK的类库目前过于臃肿,在某些微型设备上可能无法使用所有的功能,但在目前的情况下,所有的类库都不得不引用。Java9引入模块功能后,JDK、JRE、甚至JAR都可以排除不用的类库,大大减少依赖库的大小。真正的访问隔离是只要类之前是public的,就可以在整个依赖传递的范围内直接访问。但是很多时候我们需要把一些类的访问限制在一定范围内,让这些类有一些封闭性。我们可以在导入模块后执行此操作,安全地隐藏一些我们不想公开的内部实现细节。什么是模块?Java9引入的模块是在Java包的基础上引入的新的抽象层。基于包很重要,这里需要强调一下。模块结构Java模块可以由一个或多个组合在一起的Java包组成。结构可以参考下图:创建模块创建模块需要以下步骤:创建文件夹,一般是包名,如cn.felord.module。然后在cn.felord.module下创建一个module-info.java文件,称为模块描述符文件。在与模块描述符文件相同的级别创建Java包。最后,只需在创建的包下编写您的Java类文件即可。创建模块的规则创建模块还必须遵守以下规则:模块名称必须是唯一的。模块描述符文件module-info.java必须存在。包名称必须是唯一的。即使在不同的模块中,我们也不能有相同的包名。每个模块都会创建一个jar文件。对于多个jar,我们需要创建单独的模块。一个项目可以包含多个模块。模块类型模块也有类型,一共有四种。系统模块是来自JDK和JRE的模块。您可以使用java--list-modules来列出,这里是一部分:?.\java.exe--list-modulesjava.base@17java.compiler@17java.datatransfer@17java.desktop@17java.instrument@17java.logging@17java.management@17java.management.rmi@17#省略...应用模块都是在应用程序中创建的实现功能的模块。如果日常开发中涉及到的模块,应该属于这一类。自动模块化现有的jar文件,感觉就像与旧类库的兼容性。它们实际上不是模块。当我们将非模块jar添加到模块路径时,将创建一个具有该jar名称的模块。该模块具有以下属性:默认情况下导出所有包。默认情况下可以访问所有其他模块的类。未命名模块添加到类路径的jar和类。当我们将jar或类添加到类路径时,所有这些类都添加到未命名模块中,仅导出到其他未命名模块和自动模块。这意味着,应用程序模块无法访问这些类。它可以访问所有模块的类。模块描述符文件对于一个模块只有一个module-info.java,并且有格式要求。让我们找出来。声明一个模块,我们只需要在module-info.java中这样声明一个名为cn.felord的模块:modulecn.felord{}模块名要超过两个单词,以英文句点分隔。上面是一个空模块。默认情况下,模块中的所有包都是私有的,即使外部依赖项也无法访问。模块内的包遵循前面的规则,不受模块的影响。我们可以使用export关键字公开特定的包,如下所示:modulecn.felord{exportscn.felord.pkg;exportscn.felord.util;}注意cn.felord.pkg和exportscn.felord.util不能是空包,导出的包必须声明Java对象。无法导出具体的Java类。还有一种定向导出包,只暴露给某个模块。就像特制的酒和香烟。它的语法是:exportsto,,,...我们把上面的cn.felord.util导出到com.xxx:modulecn.felord{将cn.felord.pkg导出到com.xxx,com.ooo;exportscn.felord.utiltocom.xxx;}在上面的例子中,所有模块都可以访问cn.felord.pkg,但只有com.xxx模块可以访问cn.felord.util。定向向导包的范围是模块。依赖如果一个模块要访问其他模块导出的包,模块必须使用requires关键字导入要访问的包所在的模块。如上,虽然cn.felord模块打开了cn.felord.pkg包给com.ooo,但是com.ooo即使依赖cn.felord,也不能直接使用包下的类,需要这样做:modulecom.ooo{出口com.ooo.pkg;//如果你注释掉Pkg,它会变成红色。cn.felord.util下的类不能使用requirescn.felord;}requires的范围是模块。静态依赖有时我们只需要一些模块在编译时,它们在运行时是可选的。例如,测试或代码生成库。这里需要使用静态导入,关键字是requiresstatic,例如:modulecom.xxx{//去除pom依赖不能编译requiresstaticcn.felord;}本例编译时需要cn.felord是的,但它在运行时是可选的,有点像Maven中的compile。依赖传递这看起来越来越像Maven了!模块a依赖模块b,模块b依赖模块c。如果模块a要使用模块c暴露的包,需要按照前面的规则require模块c。现在我们可以借助requirestransitive来做到这一点,因为b是过去和未来之间的纽带,我们可以这样做:moduleb{exportsb.pkg;//开启依赖转移需要传递c;}modulec{exportsc.pkg}modulea{requiresb;}所有依赖b的模块都会自动依赖c导出的包,exportto导出的包优先级最高。使用服务使用uses关键字,我们可以指定我们的模块需要或使用某些服务。该服务通常是一个接口或抽象类。它不应该是一个实现类。模块com.xxx{需要com.ooo;//去除pom依赖无法编译requiresstaticcn.felord;usescom.ooo.pkg.Read;}??uses只能来自于模块自身的包或者requires,requiresstatic和requires传递的接口或者抽象类。uses用于指定所需的服务类或接口。为了提供服务,我们可以通过provides...with...语法在模块中声明一些服务实现以供其他模块使用(通过uses)。改进了OpenReflection反射API的Java9封装和安全性。使用反射,我们甚至可以访问对象的私有成员。从java9开始,默认不开启。我们可以通过open显式的给其他模块授予反射权限。opencom.xxx{}这样就可以使用反射访问com.xxx模块的所有包了。opens如果我们不想打开所有的反射访问,我们也可以使用opens关键字来指定反射可以访问的包:modulecom.xxx{openscom.xxx.reflect;}opens...to的当然,我们也可以将特定的包打开到指定的模块进行反射访问:用于反射访问的com.ooo模块。模块的总结主要是为了理解,实际应用主要用于jar级别的系统瘦身和隔离。这是用Java9以上版本搭建一个多模块的Maven或Gradle项目,按照上面的做实验就明白了。关注公众号:Felordcn获取更多资讯个人博客:https://felord.cn