前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java9来了,快来了解下JPMS基础吧!

Java9来了,快来了解下JPMS基础吧!

作者头像
ImportSource
发布2018-04-03 12:16:43
2.8K0
发布2018-04-03 12:16:43
举报
文章被收录于专栏:ImportSource

Java平台模块系统(JPMS)是Java SE 9的主要新功能。在本文中,Java Champion和JAX Londonspeaker的Stephen Colebourne介绍JPMS,并介绍它可能出现的问题。

Java Platform Module System java平台模块系统,简称JPMS。是java SE 9的最主要的一个新功能。

本文就来细细介绍下JPMS。

JPMS ,是一个全新的模块系统,作为项目Jigsaw来开发,目的就是要提高java编码的抽象级别。

这个项目的主要目标就是:

• 让java SE平台和JDK 更容易地运行到小型计算设备上;

• 提高Java SE平台实现的安全性和可维护性,特别是JDK;

• 改善应用程序性能;

• 让开发更容易的管理和组织lib和大应用程序,针对SE和EE平台。

为了实现这些目标,我们建议为Java SE平台设计和实现一套标准模块系统,并将该系统应用于平台本身和JDK。模块系统应该足够强大,可以对JDK和其他大型遗留代码库进行模块化,同时还要对开发人员友好。

然而,愿望是美好的,项目目标并不总是能够得到满足。

什么是JPMS Module?

JPMS是对Java库,语言和运行时的更改。这意味着它会影响开发人员日常代码编写的整个堆栈,因此JPMS可能会产生很大的影响。出于兼容性原因,大多数现有代码可以忽略Java SE 9中的JPMS,这可能被证明是非常有用的。

关键的概念要点是JPMS向JVM增加了一个新概念,这个概念叫模块(modules)。以前,代码被组织成字段(fields),方法(methods),类(classes),接口(interfaces)和包(packages),现在, Java SE 9中有了一个新的结构元素 - 模块。

• 类是字段和方法的容器

• package是类和接口的容器

• module 则是package的容器。

是不是感觉因垂死听。世界有点变了。

因为这是一个新的JVM元素,它意味着运行时可以应用强大的访问控制。在Java 8的时候,开发人员可以通过将某个类的方法声明为private,这样其他类就看不到该方法了。在Java 9中呢?开发人员可以让一个包(pacakge)不被其他模块看到就是一个包可以隐藏在一个模块中。

在理论上,能够隐藏软件包对于应用程序设计来说应该是一个很大的好处。不再需要把一个包命名为“impl”或“internal”,然后Javadoc声明“请不要使用此包中的类型”。不幸的是,生活不会那么简单。

但创建模块相对简单。一个模块通常只是一个jar文件,在文件的根目录下有一个module-info.class文件,就是一个模块化jar文件。该文件是从源代码库中的module-info.java文件创建的。

使用模块化jar文件涉及将jar文件添加到模块路径(modulepath )而不是类路径(classpath)。如果一个模块化jar文件在类路径上,它将不会充当模块,而module-info.class将被忽略。因此,虽然模块路径上的模块化jar文件将具有由JVM执行的隐藏包,但类路径上的模块化jar文件根本就不会有隐藏的包。

其他现有模块系统

Java一直以来都有其他模块系统,最有名的是OSGi和JBoss模块。但, JPMS与这些系统几乎没什么相似之处。

OSGi和JBoss模块在没有JVM的直接支持下而存在,但依然为模块提供一些额外的支持。它是通过在其自己的类加载器中启动每个模块来实现的,这样做是可以的,但也有自己的问题。

毫无意外的,自然而然的,这些现存的模块系统的所在组织的专家就被纳入到正式的开发JPMS的专家组。但是,这种关系并不协调。基本上,JPMS作者(Oracle)已经开始构建一个JVM扩展,可以用于可以被描述为模块的东西,而现有的模块系统却是从今天存在的大型应用程序中获取真实用例和棘手的案例的经验和价值。

阅读“模块”相关的文章时,请注意你正在阅读的文章的作者是否来自OSGi / JBoss模块设计阵营。(我从来没有主动使用OSGi或JBoss模块,尽管我已经使用Eclipse和其他在内部使用OSGi的工具。)

module-info.java

module com.opengamma.util { requires org.joda.beans; // 这是模块名,不是包名 requires com.google.guava; exports com.opengamma.util; //这是包名,不是模块名 }

module-info.java文件其实就是一个定义模块的说明书(这里只说到最重要的一些,还有其他的)。它也是一个.java文件,但是,语法与以前见过的任何.java文件无关,是完全不同的。

在编辑这个文件的时候,你通常需要思考两个关键问题:

1、该模块依赖什么。

2、它导出什么。

这个模块声明说com.opengamma.util取决于(需要)org.joda.beans和com.google.guava。它暴露一个包叫com.opengamma.util。使用模块路径(modulepath)(由JVM执行)时,所有其他软件包都将被隐藏。

与java.base(JDK的核心模块)有一个隐含的依赖关系。请注意,JDK本身也是模块化的,所以如果要依赖于Swing,XML或Logging,则需要表示依赖关系。

module org.joda.beans { requires transitive org.joda.convert; exports org.joda.beans; exports org.joda.beans.ser; }

这个模块声明说org.joda.beans依赖于(requires)org.joda.convert。 “requires transitive(依赖传递)”,而不是简单的“requires”,意味着任何需要org.joda.beans的模块也可以查看并使用org.joda.convert中的包。这里用于Joda-Beans的方法,返回类型来自Joda-Convert。如虚线显示。

module org.joda.convert { requires static com.google.guava; exports org.joda.convert; }

这个模块声明说org.joda.convert取决于(requires)com.google.guava,但是在编译时只需要“requires static”,而不是简单的“requires”。这是一个可选的依赖关系。如果Guava在模块路径上,则Joda-Convert将能够看到并使用它,如果不存在Guava,则不会发生错误。如虚线显示。

访问规则

当在使用了JVM访问规则的模块路径上运行模块化jar时,如果有下列条件时,那么程序包A中的代码可以看到包B中的类型:

• 类型为 public;

• 包B被其模块导出(exports);

• 包含程序包A的模块和包含程序包B的模块存在依赖关系。

因此,在上面的示例中,模块com.opengamma.util中的代码可以看到包裹org.joda.beans,org.joda.beans,ser,org.joda.convert和Guava导出的任何包。但是,它看不到包org.joda.convert.internal(因为它没有导出)。另外,代码模块com.google.guava看不到程序包org.joda.beans或org.joda.convert中的代码,因为没有模块化的依赖关系。

JPMS可能会出什么问题?

上述基础介绍足够简单。于是你可能想着就用这些基础知识就可以来构建应用程序了,并从“隐藏软件包”中获得一些好处。不幸的是,事情并没有这么单纯,有很多地方可能出错。

  • 所有使用module-info文件仅适用于在模块路径上使用模块化jar。为了兼容性,类路径中的所有代码都打包成一个特殊的未命名模块,没有隐藏的包和对整个JDK的完全访问。因此,隐藏软件包的安全优势至关重要。然而,JDK本身的模块总是以模块化模式运行。
  • 模块的版本不被处理。你不能将相同的模块名称加载两次 - 你不能同时加载两个相同模块的两个版本。因此,你的构建工具可以创建一个可实际运行的连贯的模块。因此,由于冲突版本引起的类路径地狱状况尚未解决。请注意,将版本号放在模块名称中是一个坏主意,它不能解决这个问题并创建其他的。
  • 两个模块可能不包含相同的包。这似乎是非常明智的,直到你认为它也适用于隐藏的包。由于隐藏的软件包没有在module-info.class中列出,所以像Maven这样的工具必须打开jar文件,以发现哪些隐藏的软件包是为了警告冲突。作为library的用户,这样的冲突将是完全令人惊讶的,因为你不会有任何迹象表明Javadoc中的隐藏包。这表明JPMS在模块之间不能提供足够的隔离,因为在这一点上尚不清晰。
  • 在编译时和运行时,模块之间不得有循环。这似乎是明智的 - 谁想让模块A依赖于B取决于依赖于A的C?但是现有项目的现实就是发生这种情况,而在类路径上并不是一个问题。例如,在上述例子中,如果Guava决定去依赖Joda-Convert,那将会发生什么呢。这种限制将使一些现有的开源项目难以迁移。
  • 反射(reflection)也发生改变了,使得非公共字段和方法将不再能够通过reflection访问。由于几乎每个框架都以这种方式使用反射,因此迁移现有代码将需要大量工作。
  • 你的依赖是否模块化?理论上,只要你的所有依赖项也是模块,你只能将代码转换为模块。对于任何一个有数百个jar文件依赖关系的大型应用程序,这就是一个大问题。 “解决方案”是自动模块,其中放置在模块路径上的普通jar文件将自动转换为模块。这个过程是有争议的,命名是一个大问题。 lib作者不应将依赖于自动模块的模块发布到公共存储库(如Maven Central),除非它们有一个自动模块名称列表。
  • 模块命名规范还没确定。 模块命名规范还没确定。我已经相信,在包含最高包之后,来命名你的模块,然后该模块“拥有”子包,是唯一的理想策略。
  • 构建系统的冲突 - 谁负责? Maven pom.xml还包含有关项目的信息。是否应该扩展以允许添加模块信息?我建议不要,因为module-info.java文件包含API的绑定部分,最好用.java代码表示,而不是像pom.xml这样的元数据。

总结

不要对Java 9中的JPMS - 模块感到太兴奋。以上只是对module-info.java文件可能的内容和JPMS的限制的总结。如果你正在考虑模块化您的lib或应用程序,请等一会儿,直到所有内容变得更清晰再说吧!

ps:本文只是一个JPMS的基础,后面会介绍更多Java平台模块系统的细节。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ImportSource 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档