拼图项目动手指南

Jigsaw项目将把模块化引入Java平台,根据原始计划,它将在12月10日完成功能。 所以我们在这里,但是拼图在哪里?

在过去的六个月中肯定发生了很多事情: 原型问世 ,内部API的迫在眉睫的删除引起了很大的骚动 , 邮件列表中充斥着有关项目设计决策的重要讨论 ,而JavaOne看到了一系列很棒的介绍性演讲 。拼图团队。 然后Java 9因拼图而延迟了半年 。

但是,让我们暂时忽略所有这些,仅关注代码。 在本文中,我们将使用一个现有的演示应用程序并将其与Java 9进行模块化。如果要继续学习,请转到GitHub ,在该处可以找到所有代码。 设置说明对于使脚本与Java 9一起运行很重要。为简便起见,我从本文的所有程序包,模块和文件夹名称中删除了前缀org.codefx.demo

拼图之前的应用

即使我竭尽全力不理会整个圣诞节,但让演示程序秉承本季的精神似乎是审慎的做法。 因此,它为出现日历建模:

  • 有一个日历,其中包含24个日历表。
  • 每张纸都知道该月的某天,并包含一个惊喜。
  • 即将到圣诞节的死亡行军是通过在控制台上打印床单(以及由此带来的惊喜)来象征的。

当然,需要首先创建日历。 它可以自己做到这一点,但它需要一种创造惊喜的方法。 为此,它得到了一个惊喜工厂清单。 main方法如下所示:

public static void main(String[] args) {List<SurpriseFactory> surpriseFactories = Arrays.asList(new ChocolateFactory(),new QuoteFactory());Calendar calendar =Calendar.createWithSurprises(surpriseFactories);System.out.println(calendar.asText());
}

该项目的初始状态绝不是拼图之前最好的。 恰恰相反,这是一个简单的起点。 它由一个包含所有必需类型的单个模块(从抽象意义上讲,不是Jigsaw解释)组成:

  • “惊喜API” – SurpriseSurpriseFactory (均为接口)
  • “日历API” – CalendarCalendarSheet用于创建日历
  • 惊喜–几个SurpriseSurpriseFactory实现
  • Main –连接并运行整个过程。

编译和运行非常简单(Java 8的命令):

# compile
javac -d classes/advent ${source files}
# package
jar -cfm jars/advent.jar ${manifest and compiled class files}
# run
java -jar jars/advent.jar

进入拼图土地

下一步虽小,但很重要。 它不会更改代码或其组织,只会将其移至Jigsaw模块中。

模组

那么什么是模块? 引用强烈推荐的模块系统状态 :

模块是命名的,自描述的代码和数据集合。 它的代码被组织为一组包含类型(即Java类和接口)的软件包。 其数据包括资源和其他种类的静态信息。

为了控制其代码如何引用其他模块中的类型,模块声明要编译和运行它需要哪些其他模块。 为了控制其他模块中的代码如何引用其包中的类型,模块声明了要导出的包中的哪个。

因此,与JAR相比,模块具有JVM可以识别的名称,声明其依赖于其他模块,并定义哪些包是其公共API的一部分。

名称

模块的名称可以是任意的。 但是为了确保唯一性,建议坚持使用包的反向URL命名模式。 因此,虽然这不是必需的,但通常意味着模块名称是它包含的软件包的前缀。

依存关系

一个模块列出了要编译和运行的其他模块。 对于应用程序和库模块而言,这都是正确的,但对于JDK本身中的模块而言,这是正确的,它被分成了大约80个(请使用java -listmods )。

再次从设计概述中:

当一个模块直接依赖于模块图中的另一个模块时,第一个模块中的代码将能够引用第二个模块中的类型。 因此,我们说第一模块读取第二模块,或者等效地,第二模块被第一模块读取

[…]

模块系统确保每个依赖关系都由另一个模块准确地满足,没有两个模块互相读取,每个模块最多读取一个定义给定程序包的模块,并且定义同名程序包的模块不会互相干扰。

当违反任何一个属性时,模块系统将拒绝编译或启动代码。 这是对脆弱类路径的巨大改进,在脆弱类路径中,例如丢失的JAR仅在运行时才发现,从而使应用程序崩溃。

还需要指出的是,只有模块直接依赖于另一个模块,才能访问另一个模块的类型。 因此,如果A依赖于B ,而B依赖于C ,那么除非明确要求A ,否则A将无法访问C。

出口产品

一个模块列出了它导出的软件包。 这些包中的公共类型只能从模块外部访问。

这意味着public不再是真正的公众。 非导出包中的公共类型与导出包中的非公共类型一样,对外界隐藏。 它比当今的私有包类型更加隐藏,因为模块系统甚至不允许反射访问它们。 由于拼图是当前实现的,命令行标志是解决此问题的唯一方法。

实作

为了能够创建模块,项目需要在其根源目录中包含module-info.java

module advent {// no imports or exports
}

等等,我不是说我们也必须声明对JDK模块的依赖吗? 那么,为什么我们在这里没有提到什么呢? 所有Java代码都需要Object ,并且该类以及演示使用的其他少数几个类也是模块java.base一部分。 因此,实际上每个 Java模块都依赖于java.base ,这导致Jigsaw团队决定自动要求它。 因此,我们不必明确提及它。

最大的变化是要编译和运行的脚本(Java 9的命令):

# compile (include module-info.java)
javac -d classes/advent ${source files}
# package (add module-info.class and specify main class)
jar -c \--file=mods/advent.jar \--main-class=advent.Main \${compiled class files}
# run (specify a module path and simply name to module to run)
java -mp mods -m advent

我们可以看到编译几乎相同–我们只需要在类列表中包括新的module-info.java

jar命令将创建所谓的模块化JAR,即包含模块的JAR。 与之前不同,我们不再需要任何清单,而是可以直接指定主类。 注意如何在目录mods创建JAR。

完全不同的是启动应用程序的方式。 这样做的目的是告诉Java在哪里可以找到应用程序模块(使用-mp mods ,这称为模块路径 ),以及我们想启动哪个模块(使用-m advent )。

分成模块

现在是时候真正了解Jigsaw并将其拆分为单独的模块了。

虚构理由

“惊喜API”(即SurpriseSurpriseFactory取得了巨大的成功,我们希望将其与整体分离。

创造惊喜的工厂非常活跃。 这里要做很多工作,它们经常更改,并且使用的工厂因版本而异。 因此,我们想隔离它们。

同时,我们计划创建一个大型的圣诞节应用程序,日历仅是其中的一部分。 因此,我们也希望为此提供一个单独的模块。

我们最终得到以下模块:

  • 惊喜Surprise and SurpriseFactory
  • 日历 –日历,使用Surprise API
  • 工厂SurpriseFactory实现
  • main –原来的应用程序,现在已经镂空到Main

通过查看它们之间的依赖关系,我们可以发现惊喜并不取决于其他模块。 日历工厂都使用它的类型,因此必须依赖它。 最后, main使用工厂来创建日历,因此它依赖于两者。

图1_50066

实作

第一步是重新组织源代码。 我们将遵循官方快速入门指南建议的目录结构,并将所有模块放在src的自己的文件夹中:

src- advent.calendar: the "calendar" module- org ...module-info.java- advent.factories: the "factories" module- org ...module-info.java- advent.surprise: the "surprise" module- org ...module-info.java- advent: the "main" module- org ...module-info.java
.gitignore
compileAndRun.sh
LICENSE
README

为了保持可读性,我删节了org下面的文件夹。 缺少的是软件包以及每个模块的最终源文件。 完整地在GitHub上查看。

现在,让我们看看这些模块信息必须包含什么以及如何编译和运行应用程序。

没有必要的条款,因为Surprise没有依赖性。 (除了java.base ,它始终是隐式必需的。)它导出包advent.surprise因为它包含两个类SurpriseSurpriseFactory

因此module-info.java如下所示:

module advent.surprise {// requires no other modules// publicly accessible packagesexports advent.surprise;
}

编译和打包与上一节非常相似。 实际上,这甚至更容易,因为意外事件不包含任何主要类别:

# compile
javac -d classes/advent.surprise ${source files}
# package
jar -c --file=mods/advent.surprise.jar ${compiled class files}

日历使用来自Surprise API的类型,因此模块必须依赖Surprise 。 向模块添加requires advent.surprise即可实现。

该模块的API由Calendar类组成。 为了使其可公开访问,必须导出包含软件包advent.calendar 。 请注意,同一包私有的CalendarSheet在模块外部将不可见。

但有一个附加的扭曲:我们刚刚作出Calendar.createWithSurprises( List<SurpriseFactory> )公布,从惊喜模块暴露类型。 因此,除非读取日历的模块也需要惊讶 ,否则Jigsaw将阻止它们访问这些类型,这将导致编译和运行时错误。

将require子句标记为public可解决此问题。 有了它,任何依赖日历的模块也会让人吃惊 。 这称为隐式可读性

最终的模块信息如下所示:

module advent.calendar {// required modulesrequires public advent.surprise;// publicly accessible packagesexports advent.calendar;
}

编译几乎像以前一样,但是当然必须在此反映对惊奇的依赖。 为此,将编译器指向目录mods就足够了,因为它包含所需的模块:

# compile (point to folder with required modules)
javac -mp mods \-d classes/advent.calendar \${source files}
# package
jar -c \--file=mods/advent.calendar.jar \${compiled class files}

该工厂实现SurpriseFactory所以这个模块必须依靠惊喜 。 并且由于它们从已发布的方法返回Surprise实例,因此与上述相同的思路导致了requires public子句的出现。

可以在包advent.factories找到工厂,因此必须将其导出。 请注意,在另一个模块中找到的公共类AbstractSurpriseFactory在此模块外部无法访问。

这样我们得到:

module advent.factories {// required modulesrequires public advent.surprise;// publicly accessible packagesexports advent.factories;
}

编译和打包类似于日历

我们的应用程序需要日历工厂这两个模块进行编译和运行。 它没有要导出的API。

module advent {// required modulesrequires advent.calendar;requires advent.factories;// no exports
}

编译和打包与上一节的单个模块相似,不同之处在于编译器需要知道在哪里寻找所需的模块:

#compile
javac -mp mods \-d classes/advent \${source files}
# package
jar -c \--file=mods/advent.jar \--main-class=advent.Main \${compiled class files}
# run
java -mp mods -m advent

服务

拼图通过实现服务定位器模式实现松散耦合,在该模式中 ,模块系统本身充当定位器。 让我们看看情况如何 。

虚构理由

最近有人读了一篇关于冷松耦合的博客文章。 然后她从上面看我们的代码,抱怨主机工厂之间的紧密关系。 为什么还要知道工厂

因为…

public static void main(String[] args) {List<SurpriseFactory> surpriseFactories = Arrays.asList(new ChocolateFactory(),new QuoteFactory());Calendar calendar =Calendar.createWithSurprises(surpriseFactories);System.out.println(calendar.asText());
}

真? 只是为了实例化完美抽象的某些实现( SurpriseFactory )?

而且我们知道她是对的。 让其他人为我们提供实现将消除直接依赖。 更好的是,如果说中间人能够在模块路径上找到所有实现,则可以通过在启动之前添加或删除模块来轻松配置日历的惊喜。

拼图确实可以做到这一点。 我们可以有一个模块来指定它提供接口的实现。 另一个模块可以表示它使用所述接口,并使用ServiceLocator查找所有实现。

我们利用这个机会将工厂分割成巧克力,然后报价并最终得到以下模块和依赖项:

  • 惊喜Surprise and SurpriseFactory
  • 日历 –日历,使用Surprise API
  • 巧克力ChocolateFactory即服务
  • quoteQuoteFactory即服务
  • 主要 –应用程序; 不再需要单个工厂

图2_50066

实作

第一步是重新组织源代码。 之前的唯一变化是src/advent.factoriessrc/advent.factory.chocolatesrc/advent.factory.quote

让我们看一下各个模块。

两者都没有改变。

除某些名称外,两个模块都是相同的。 让我们看一下巧克力,因为它更美味。

工厂以前一样,该模块requires public 惊喜模块。

更有趣的是其出口。 它提供了SurpriseFactory的实现,即ChocolateFactory ,其指定如下:

provides advent.surprise.SurpriseFactorywith advent.factory.chocolate.ChocolateFactory;

由于此类是其公共API的全部,因此不需要导出其他任何内容。 因此,没有其他出口条款是必要的。

我们最终得到:

module advent.factory.chocolate {// list the required modulesrequires public advent.surprise;// specify which class provides which serviceprovides advent.surprise.SurpriseFactorywith advent.factory.chocolate.ChocolateFactory;
}

编译和打包很简单:

javac -mp mods \-d classes/advent.factory.chocolate \${source files}
jar -c \--file mods/advent.factory.chocolate.jar \${compiled class files}

关于main的最有趣的部分是它如何使用ServiceLocator查找SurpriseFactory的实现。 从其主要方法 :

List surpriseFactories = new ArrayList<>();
ServiceLoader.load(SurpriseFactory.class).forEach(surpriseFactories::add);

我们的应用程序现在仅需要日历,但必须指定它使用SurpriseFactory 。 它没有要导出的API。

module advent {// list the required modulesrequires advent.calendar;// list the used servicesuses advent.surprise.SurpriseFactory;// exports no functionality
}

编译和执行与以前一样。

我们确实可以通过简单地从模块路径中删除工厂模块之一来更改日历最终将包含的惊喜。 整齐!

摘要

就是这样了。 我们已经看到了如何将单片应用程序移动到单个模块中以及如何将其拆分为多个模块。 我们甚至使用服务定位器将我们的应用程序与服务的具体实现分离开来。 所有这些都在GitHub上,因此请查看更多代码!

但是还有更多要讨论的! 拼图带来了一些不兼容问题,但也解决了许多不兼容问题。 而且我们还没有讨论反射如何与模块系统交互以及如何迁移外部依赖项。

如果您对这些主题感兴趣,请在我的博客上观看Jigsaw标签 ,因为在接下来的几个月中我一定会写关于它们的。

翻译自: https://www.javacodegeeks.com/2015/12/project-jigsaw-hands-guide.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/356033.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java实现回文验证_LeetCode 精选 TOP 面试题(Java 实现)—— 验证回文串

文章目录一、题目描述1.1 题目验证回文串给定一个字符串&#xff0c;验证它是否是回文串&#xff0c;只考虑字母和数字字符&#xff0c;可以忽略字母的大小写。说明&#xff1a;本题中&#xff0c;我们将空字符串定义为有效的回文串。示例 1:输入: "A man, a plan, a cana…

Yarn 和 Npm 命令行切换 摘录

原文作者: Gant Laborde原文地址: https://shift.infinite.red/np...中文翻译: 文蔺译文地址:http://www.wemlion.com/2016/n... 好&#xff0c;想必你对新的 JavaScript 包管理工具 yarn 已经有所耳闻&#xff0c;并已通过 npm i -g yarn 进行了安装&#xff0c;现在想知道怎么…

Something about Giraffe (II)

先解決老師留的幾個問題再說吧&#xff0c;23333 XD.Giraffe 是一個將 Deep Reinforcement Learning 和 Neural Network 结合到一起的Chess AI 程式。 作者 Matthew Lai 是一個了不起的人&#xff0c;目前就職於 Google Deepmind. 隨著AlphaGo席捲全球&#xff0c;隨著Deep lea…

php 时间选择,PHP-在学说2中的日期之间选择条目

PHP-在学说2中的日期之间选择条目我将因这个无法修复的最小错误而发疯。 我想在两天之间选择条目&#xff0c;下面的示例说明了我所有的失败&#xff1a;选择1。$qb->where(e.fecha > . $monday->format(Y-m-d));$qb->andWhere(e.fecha < . $sunday->forma…

threadlocal线程_线程故事:Web应用程序中的ThreadLocal

threadlocal线程本周&#xff0c;我花了一些合理的时间来消除Web应用程序中的所有ThreadLocal变量。 原因是他们造成了类加载器泄漏&#xff0c;我们不能再适当地取消部署我们的应用程序。 取消部署应用程序后&#xff0c;当GC根目录继续引用应用程序对象时&#xff0c;将发生类…

java开发门禁指纹,指纹门禁

大兀人脸识别控制系统USB指纹仪DW9拓扑图(指纹为java系统直接采集、云或控制板直接比对&#xff0c;无需二次注册&#xff0c;人脸为htttp上传服务器或rtsp视频流比对方案)大兀活体指纹门禁采用BS架构&#xff0c;已集成进大兀人脸识别系统&#xff0c;如二次开发可直接调用jav…

使用Selenide进行有效的UI测试

等待奇迹 圣诞节是奇迹的时刻。 在新的一年的前夕&#xff0c;我们都为下一年制定了计划。 我们希望所有问题都将在最后一年消失&#xff0c;在来年出现奇迹。 每个Java开发人员都梦想着创造一个奇迹&#xff0c;使他成为世界上最有效的Java开发人员。 我想向你展示这样的奇…

android之seekbar

做了一个拖动滑块改变数值的demo 下附上代码: <SeekBar android:id"id/myseekbar" android:layout_width"match_parent" android:layout_marginTop"20dp" android:thumb"drawable/slider" android:layout_height&qu…

php 微信小程序 循环 多选,微信小程序实现多选功能

本文为大家分享了微信小程序实现多选功能的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下代码&#xff1a;{{num 1}}/{{quesyion.length}}{{question[num][0]}}A {{question[num][1]}}B {{question[num][2]}}C {{question[num][3]}}D {{question[num][4]}}正确答案…

今天在看慕课网的java学习路径

new对象是静态加载类 在编译时刻就需要加载所有可能使用的类 如果有有一个类没有 其他的类也无法使用 通过动态加载类可以解决这个问题 class for name 打印异常栈轨迹Stack Trace。说起栈轨迹,可能很多人和我一样,第一反应就是printStackTrace()方法 classforName //加载…

php+反序列化代码执行漏洞,PHP反序列化漏洞

0x001 漏洞产生原理在反序列化的过程中自动触发了某些魔术方法。未对用户输入的序列化字符串进行检测&#xff0c;导致攻击者可以控制反序列化过程&#xff0c;从而导致XSS、代码执行、文件写入、文件读取等不可控后果。0x002 漏洞触发条件一般只能通过代码审计的方式挖掘该漏洞…

java 应用程序 gui_Java GUI应用程序关闭陷阱

java 应用程序 gui最近&#xff0c;我遇到了一个或两个Java GUI应用程序在关闭时无法关闭的问题。 他们似乎是一个过程&#xff0c;消耗着计算机资源。 今天&#xff0c;我深入探究了问题的根源&#xff0c;这是一个我以前不曾意识到的棘手陷阱&#xff0c;所以我想我会分享一下…

php动态成本管理,网上审批系统环境下的动态目标成本管理

1.预测目标价格和销售量目标价格是根据市场可接受的价格范围、企业自身的竞争优势和企业的经营理念等因素来确定的。在确定目标价格的同时&#xff0c;需要确定在这种价格下企业的预计销售量&#xff0c;因为销售量影响生产量&#xff0c;生产量影响作业量&#xff0c;作业量影…

学习进度(第三周)

所花时间 9.5h 代码量 350行 博客量 2篇 了解到的知识点 Hadoop HBase数据库 转载于:https://www.cnblogs.com/yifengyifeng/p/6542224.html

Java中的值类型:为什么它们不可变?

值类型不必是不变的。 但是他们是。 在上一篇文章中&#xff0c;我讨论了Java中指针与引用之间的区别以及如何传递方法参数&#xff08;按值传递或按引用传递&#xff09;。 这些与Java中尚不存在的值类型密切相关&#xff08;尚未&#xff09;。 John Rose&#xff0c;Brian…

mac设置首页访问php,mac系统下php项目除了首页全访问不了

有网友反映mac配置apache的有问题&#xff0c;除了首页全都访问不了&#xff0c;小编找来了相关的解决办法&#xff0c;有需要的朋友可以来了解一下。httpd.conf配置文件中加载了mod_rewrite.so模块AllowOverride None 将None改为 AllDocumentRoot "/Library/WebServer/Do…

java filter 模式,Java设计模式----过滤器模式(挑三拣四)

过滤器模式描述拦截过滤器模式(Intercepting Filter Pattern)用于对应用程序的请求或响应做一些预处理/后处理。定义过滤器&#xff0c;并在把请求传给实际目标应用程序之前应用在请求上。过滤器可以做认证/授权/记录日志&#xff0c;或者跟踪请求&#xff0c;然后把请求传给相…

表单

表单本身也是DOM树 表单的输入框、下拉框等可以接收用户输入&#xff0c;所以用JavaScript来操作表单&#xff0c;可以获得用户输入的内容&#xff0c;或者对一个输入框设置新的内容。 HTML表单的输入控件主要有以下几种&#xff1a; 文本框&#xff0c;对应的<input type&q…

java项目processes设置,Jvm调优和SpringBoot项目优化的详细教程

参考文章1、先看一下未设置JVM参数的情况&#xff0c;默认情况下&#xff0c;没有设置任何Jvm参数。2、设置Jvm参数。配置参数&#xff1a;关于这些设置的JVM参数是什么意思&#xff0c;参考Jvm调优。-XX:MetaspaceSize128m (元空间默认大小)-XX:MaxMetaspaceSize128m (元空间最…

Git问题Everything up-to-date解决

Git问题Everything up-to-date解决 【自己的亲身错误体验】 我的上一篇博客&#xff0c;说了怎么上传一个项目到git远程上面。今天我写好一个小栗子&#xff0c;准备再次上传的时候。我依旧是放在我的F:\github\js- 这个和我的github上的远程仓库相同名称的文件中的 在这里空白…