拼图项目动手指南

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…

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]}}正确答案…

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

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

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

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

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上的远程仓库相同名称的文件中的 在这里空白…

java超出gc开销限制_超出了GC开销限制– Java堆分析

java超出gc开销限制这篇文章是我们原来的GC开销超出问题模式的延续。 正确的Java堆分析对于消除O​​utOfMemoryError&#xff1a;GC开销问题至关重要。 如果您不熟悉此Java HotSpot 1.6错误&#xff0c;建议您首先阅读有关此主题的第一篇文章 。 本文将为您提供一个示例程序和…

java自定义一个timeout,Timeout操作符 RxJava 学习笔记二十一

timeout用于检测在给定时间内observables没有及时响应。如果指定的时间量没有发出任何项目&#xff0c;则超时会使observables失败并出现TimeoutException。我们将从debounce的示例中重用我们的observable来演示超时。输出&#xff1a;只要值不超过200ms&#xff0c;就会输出。…

用xshell ssh连接测试服务器时候出的问题

问题还原&#xff1a;用ssh连接测试服务器 给我结结实实报了个错 FBIwarning: ----------------------------------------------------------------------------------------------------------------------------以上 问题还原 解决过程&#xff1a;百度 博客地址http://blog.…

maven集成spring_Maven集成测试和Spring Restful Services

maven集成spring介绍 我的原始博客通过一个非常简单的示例展示了如何分离Maven单元和集成测试。 http://johndobie.blogspot.com/2011/06/seperating-maven-unit-integration-tests.html此后&#xff0c;许多人要求我提供比最初使用的示例更实际的示例。 这篇文章显示了如何在实…

WebService.asmx架设后,显示调用按钮的方法

我们在VS本地调试WebService的时候&#xff0c;页面是可以调试的&#xff0c;如下图 但是在发布以后&#xff0c;通过浏览器访问服务器上的地址&#xff0c;是不能调试你的程序的&#xff0c;就变成了这个样子&#xff1a; 可以通过修改服务器上的web.config达到远程调试的效果…

复习者-Java EE 7概览

随着红帽JBoss企业应用平台7&#xff08;EAP 7&#xff09;的出现&#xff0c;甚至最近刚刚通过Java EE 7认证的WebLogic服务器&#xff0c;我认为现在应该为您重新介绍Java Enterprise Edition 7的时候了功能&#xff0c;并指向更多资源。 Java EE 7 –开发人员的生产力&…

按窗口文件php代码,在Windows命令行窗口中输入并运行PHP代码片段(不需要php文件)的方法...

有时候只是简单的为了测试某个php函数的效果&#xff0c;以前总是需要建一个php文件&#xff0c;复制这个文件的路径&#xff0c;再通过web访问或者用php命令执行这个php文件。一直想要怎么才能不用创建文件&#xff0c;才能直接执行PHP代码片段呢&#xff1f;终于找到了方法&a…

adf435编程_动态ADF列车:以编程方式添加列车停靠点

adf435编程我将展示如何以编程方式“即时”将火车停靠站添加到ADF火车中。 在我的用例中&#xff0c;我有一些票务预订应用程序。 它具有训练模型的有限任务流。 在火车的第一站&#xff0c;用户输入乘客数量&#xff0c;在随后的站点&#xff0c;他们输入一些乘客信息。 必须根…

JAVA消息服务JMS规范及原理详解

一、简介 JMS即Java消息服务&#xff08;Java Message Service&#xff09;应用程序接口&#xff0c;是一个Java平台中关于面向消息中间件&#xff08;MOM&#xff09;的API&#xff0c;用于在两个应用程序之间&#xff0c;或分布式系统中发送消息&#xff0c;进行异步通信。Ja…

Fiddle用于移动端抓包

一、什么情况下可以用到 1、调查参考其他移动端网站的抓包&#xff0c;他们传输方式。如微信上京东的智能机器人的包、移动端的请求接口格式、如何实现的效果等。 2、调试本地移动端页面的测试页面效果是否有问题。如:页面的样式、js、结构等。 二、操作(http) a、安装猎豹免费…

php中得到复选框的数据的代码,表单复选框向PHP传输数据的代码

表单复选框向PHP传输数据的代码表单复选框就是checkbox1、checkbox的应用复制代码 代码如下:2、由于我传输的是在php循环中产生的数组&#xff0c;因此value也要设成变量&#xff1a;for($i0;$i<10;$i){?>">]" value"要传输的内容">]" v…