Java 9模块服务

接线与查找

Java长期以来都有一个ServiceLoader类。 它是在1.6中引入的,但是自Java 1.2以来就使用了类似的技术。 一些软件组件使用了它,但是使用并不广泛。 它可以用于模块化应用程序(甚至更多),并提供一种使用应用程序不依赖于编译时间的插件扩展应用程序的方法。 而且,这些服务的配置非常简单:只需将其放在类/模块路径上即可。 我们将看到详细信息。

服务加载器可以定位某些接口的实现。 在EE环境中,还有其他方法可以配置实现。 在非EE环境中,Spring变得无处不在,它具有相似的解决方案,尽管对相似但并非完全相同的问题的解决方案并不完全相同。 Spring提供的控制反转(IoC)和依赖注入(DI)是解决不同组件布线的解决方案,并且是行业最佳实践,如何将布线描述/代码与功能的实际实现分开该类必须执行。

实际上,Spring还支持使用服务加载器,因此您可以连接由服务加载器定位并实例化的实现。 您可以在此处找到一篇简短且写得很好的文章。

ServiceLoader在我们将其注入需要它的组件之前,是关于如何找到实现的更多信息。 初级程序员有时会错误地将两者混为一谈,这并非没有道理:它们之间有着密切的联系。

也许由于这个原因,大多数应用程序,至少我所看到的那些应用程序,并没有将接线和实现的发现分开。 这些应用程序通常使用Spring配置进行查找和接线,这没关系。 尽管这是一种简化,但我们应该对此感到满意并感到高兴。 我们不应仅仅因为可以就将两个功能分开。 大多数应用程序不需要将它们分开。 它们整齐地坐在Spring应用程序的XML配置的简单行上。

我们应该在需要的抽象水平上进行编程,但绝对不要再抽象。

是的,这句话是爱因斯坦的一句话的解释。 如果您考虑一下,您还可以意识到,此声明只不过是KISS原理(保持简单而愚蠢)。 代码,不是你。

ServiceLoader查找某个类的实现。 并非所有可能在类路径上的实现。 它仅查找那些“广告”的广告。 (我将在稍后说明“公告”的含义。)Java程序无法遍历类路径上的所有类,或者它们可以遍历吗?

浏览类路径

本节稍作绕道,但重要的是要理解ServiceLoader为何以这种方式工作,甚至在我们讨论其工作方式之前。

Java代码无法查询类加载器以列出类路径上的所有类。 您可能会说我撒谎,因为Spring确实浏览了这些类并自动找到了实现候选者。 春天实际上是作弊。 我会告诉你它是怎么做的。 现在,请接受不能浏览类路径。 如果查看ClassLoader的文档,则找不到会返回类的数组,流或集合的任何方法。 您可以获取软件包的数组,但是即使从软件包中也无法获取类。

其原因是Java处理类的抽象级别。 类加载器将类加载到JVM中,而JVM不在乎。 它不假定实际的类在文件中。 有很多应用程序可以从文件而不是文件中加载类。 实际上,大多数应用程序都从不同的媒体加载某些类。 还有您的程序,您可能不知道。 您是否曾经使用过Spring,Hibernate或其他框架? 这些框架大多数都在运行时创建代理对象,并使用特殊的类加载器从内存中加载这些对象。 类加载器无法告诉您它支持的框架是否会创建一个新对象。 在这种情况下,类路径不是静态的。 这些特殊类加载器甚至没有类路径之类的东西。 他们动态地找到类。

好的。 说得好,并详细介绍。 但是再说一遍:Spring如何找到类? Spring实际上做出了一个大胆的假设。 假定类加载器是一种特殊的加载器: URLClassLoader 。 (并且正如Nicolai Parlog在他的文章中所写,Java 9不再适用。)它与包含URL的类路径一起使用,并且可以返回URL数组。

ServiceLoader不会做出这样的假设,因此不会浏览类。

ServiceLoader如何查找类

ServiceLoader可以查找和实例化实现特定接口的类。 当我们调用静态方法ServiceLoader.load(interfaceKlass) ,它将返回实现此接口的类的“列表”。 我在引号之间使用“列表”,因为从技术上讲,它返回一个ServiceLoader实例,该实例本身实现Iterable因此我们可以迭代实现该接口的类的实例。 迭代通常在for循环中完成,该循环在(:)冒号之后调用load()方法。

为了成功找到实例,包含实现的JAR文件应该在目录META-INF/service具有一个特殊文件,该文件应具有接口的完全限定名称。 是的,名称中包含点,并且没有任何特定的文件扩展名,但是,它必须是文本文件。 它必须包含在该JAR文件中实现接口的类的标准名称。

ServiceLoader调用ClassLoader方法findResources来获取文件的URL,并读取类的名称,然后再次要求ClassLoader加载这些类。 这些类应具有一个公共的零参数构造函数,以便ServiceLoader可以实例化每个实例。

使这些文件包含类的名称,以使用资源加载来搭载类和实例化,但效果并不理想。
Java 9在保留烦人的META-INF/services解决方案的同时引入了一种新方法。 随着拼图的引入,我们有了模块,而模块有了模块描述符。 模块可以定义ServiceLoader可以加载的服务,模块还可以指定可能需要通过ServiceLoader加载哪些服务。 发现服务接口实现的这种新方式从文本资源转移到Java代码。 它的纯粹优点是可以在编译期间或模块加载时间识别与错误名称相关的编码错误,以使失败的代码更快地失败。

为了使事情变得更加灵活,或者只是使它们变得无用的变得更加复杂(将来会告诉人们),如果该类不是服务接口的实现,但确实具有返回该类实例的public static provider()方法,则Java 9也可以使用实现该接口。 (顺便说一句:在这种情况下,提供程序类甚至可以根据需要实现服务接口,但是通常它是工厂,所以为什么要这样做。请注意SRP。)

样例代码

您可以从https://github.com/verhas/module-test下载多模块Maven项目。

该项目包含三个模块ConsumerProviderServiceInterface 。 使用者调用ServiceLoader并使用服务,该服务由ServiceInterface模块中的接口javax0.serviceinterface.ServiceInterface定义,并在Provider模块中实现。 下图显示了代码的结构:

module-info文件包含以下声明:

module Provider {requires ServiceInterface;provides javax0.serviceinterface.ServiceInterfacewith javax0.serviceprovider.Provider;
}module Consumer {requires ServiceInterface;uses javax0.serviceinterface.ServiceInterface;
}module ServiceInterface {exports javax0.serviceinterface;
}

陷阱

在这里,我将告诉您我在创建此非常简单的示例时所犯的一些愚蠢的错误,以便您可以从错误中学习,而不必重复这些错误。 首先, ServiceLoader中的Java 9 JDK文档中有一句话是:

另外,如果服务不在应用程序模块中,则模块声明必须具有一个require指令,该指令指定导出服务的模块。

我不知道它想说什么,但是对我来说意味着什么是不正确的。 也许我误解了这句话,这很可能。

看我们的示例, Consumer模块使用实现javax0.serviceinterface.ServiceInterface接口的东西。 这实际上是Provider模块及其中的实现,但是它仅在运行时确定,并且可以由任何其他合适的实现替换。 因此,它需要接口,因此必须在requires ServiceInterface模块的模块信息文件中具有ServiceInterface指令。 它不需要Provider模块! Provider模块类似地依赖于ServiceInterface模块,并且必须要求它。 ServiceInterface模块不需要任何内容​​。 它仅导出包含接口的包。

同样重要的是要注意,不需要Provider模块和Consumer模块都可以导出任何程序包。 Provider提供由接口声明的服务,并由模块信息文件中以with关键字命名的类实现。 它为世界提供了这一类,仅此而已。 如果仅提供此类,则导出包含它的包将是多余的,并且可能不必要地打开同一包中可能发生但属于模块内部的类。 使用–m选项从命令行调用Consumer ,并且它也不需要模块导出任何包。
像启动程序一样的命令是

java -p Consumer/target/Consumer-1.0.0-SNAPSHOT.jar:ServiceInterface/target/ServiceInterface-1.0.0-SNAPSHOT.jar:Provider/target/Provider-1.0.0-SNAPSHOT.jar -m Consumer/javax0.serviceconsumer.Consumer

它可以在成功执行mvn install命令后执行。 请注意,maven编译器插件必须至少为3.6版,否则,在编译期间,ServiceInterface-1.0.0-SNAPSHOT.jar将位于类路径而不是模块路径上,并且编译将无法找到module-info.class文件。

有什么意义

当应用程序仅在运行时与某些模块连接时,才可以使用ServiceLoader 。 一个典型的示例是带有插件的应用程序。 当我将ScriptBasic for Java从Java 7移植到Java 9时,我自己就参与了该练习。BASIC语言解释器可以由包含公共静态方法的类扩展,并且必须将其注释为BasicFunction 。 最后一个版本要求嵌入解释器的主机应用程序列出所有在代码中调用API的扩展类。 这是多余的,不需要的。 ServiceLoader可以找到在ClassSetProvider定义了接口( ClassSetProvider )的服务实现,然后主程序可以依次调用服务实现并注册在集合中返回的类。 这样,主机应用程序无需了解有关扩展类的任何信息,将扩展类放在模块路径上就可以了,每个扩展类都可以提供服务。

JDK本身也使用此机制来定位记录器。 新的Java 9 JDK包含System.LoggerFinder类,可以通过任何模块将其实现为服务,并且如果存在实现,则ServiceLoader可以找到方法System.getLogger()将找到该类。 这样,日志记录不绑定到JDK,也不在编译时绑定到库。 在运行时和应用程序之间提供记录器就足够了,应用程序使用的库和JDK都将使用相同的记录工具。

通过服务加载机制中的所有这些更改,并使之成为语言的一部分,而不再依赖于资源加载,人们可能希望这种类型的服务发现将获得动力,并将像以前一样被广泛使用。

翻译自: https://www.javacodegeeks.com/2018/01/java-9-module-services.html

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

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

相关文章

简单排序--插入排序

插入排序&#xff1a; public void sort(){int in,out,temp;for(out1;out<nElements;out){temp arr[out];in out;while(in>0&&arr[in-1]>temp){arr[in] arr[in-1];//待插入的数据比其之前的数字大的右移&#xff0c;从小到大排序--in;//依次左移}arr[in] …

一些工厂实例

我时不时地发现自己摸索了一些旧的代码&#xff0c;以找到示例“我在哪里做过工厂一样的事情”。 上周再次发生这种情况时&#xff0c;我决定只查找所有示例&#xff0c;并创建一个示例项目和有关它的博客文章。 所以在这篇文章中&#xff0c;我&#xff1a; 从简单的“原始…

eclipse工程导入Android Studio

在eclipse中选中要导出的工程&#xff08;此工程在eclipse中最好不要有错误&#xff09;&#xff0c;右键选择Export->Generate Gradle build files 在本地工程里生成了一个build.gradle文件。 在AS中选择 导入成功后会生成一个import-summary.txt 导入成功。

idea中drl文件_得分DRL:在OptaPlanner中更快,更轻松

idea中drl文件对于OptaPlanner &#xff08; Drools Planner&#xff09;6.0.0.Beta1&#xff0c;我已经用更优雅的ConstraintMatch系统替换了ConstraintOccurrence。 结果是您的DRL评分文件为&#xff1a; 快多了 更容易读写 错误的发生率要低得多&#xff0c;因为它们使得…

超棒的Glide图片加载

GitHub开源&#xff1a;https://github.com/bumptech/glide 超棒的工具&#xff0c;竟然还可以加载.gif !!Glide完全基于Picasso&#xff0c;沿袭了其简洁风格&#xff0c;并在其基础上做了大量优化与改进。 1、Glide默认的Bitmap格式是RGB_565&#xff0c;而Picasso默认ARGB_…

开源GraphView的使用--数据统计

最近做室内定位需要绘出加速度传感器输出的三个方向的加速度曲线&#xff0c;找到了开源https://github.com/jjoe64/GraphView-Demos&#xff0c;省去了要重新学MatLab **。 在http://www.android-graphview.org/download--getting-started.html下载.jar包。 1、GraphView的使…

我的对象命名

这是最常见的辩论之一。 大多数人对此主题有自己的见解&#xff0c;却没人能真正说出哪个是正确的。 我当然不能&#xff0c;但是尽管如此&#xff0c;我还是决定与大家分享我的想法&#xff0c;投入两美分&#xff0c;也许对某人会有帮助。 当我创建一个新类时&#xff0c;我…

netbeans 定制代码_将NetBeans代码模板弯曲到我的意愿

netbeans 定制代码任何阅读过我关于NetBeans的文章的人都知道&#xff0c;我真的很喜欢NetBeans的许多功能。 但是&#xff0c;最近&#xff0c;我发现自己对NetBeans特定功能的特定问题越来越恼火。 最终&#xff0c;它使我烦恼不已&#xff0c;促使我开始研究如何根据自己的喜…

Java 9概览

对于许多Java 9来说&#xff0c;它似乎是一个维护版本&#xff0c;它推动了不能在Java 8中实现的项目Jigsaw。但是&#xff0c;随着JDK中的新模块系统以及与之相关的许多内部更改&#xff0c;Java 9也带来了开发人员工具箱中一些很棒的新内容。 以下是重点内容&#xff1a; JS…

adf开发_了解ADF Faces clientComponent属性

adf开发我相信大多数ADF开发人员都知道ADF Faces属性clientComponent 。 在本文中&#xff0c;我将展示该属性实际上如何影响组件渲染以及它如何改变其行为。 让我们开始考虑一个非常简单的示例&#xff1a; <af:inputText label"Label 1" id"it1" /&g…

Picocli 2.0:事半功倍

介绍 Picocli是一个单文件命令行解析框架&#xff0c;它使您几乎不需要任何代码即可创建命令行应用程序。 使用Option或Parameters注释应用程序中的字段&#xff0c;picocli将分别使用命令行选项和位置参数填充这些字段。 例如&#xff1a; Command(name "Greet", …

移动通信—无线波传播

无线波传播 发射天线或自然辐射源所辐射的无线电波,通过自然条件下的媒质到达接受天线的过程称为无线电波传播。无线电波与可见光、X射线与γ射线-.样同属于电磁波,它们都是以电场和磁场为其特征的- -种电场震动。电磁波的频谱范围很宽,从几赫到3X 1023Hz(波长从几十兆米到10-9…

使用Dropwizard度量标准监视和测量无功应用

在上一篇文章中&#xff0c;我们创建了一个简单的索引代码&#xff0c;该代码可以对ElasticSearch进行数千个并发请求。 监视系统性能的唯一方法是老式的日志记录语句&#xff1a; .window(Duration.ofSeconds(1)) .flatMap(Flux::count) .subscribe(winSize -> log.debug(…

移动通信-多径效应,多普勒效应,菲涅尔区,阴影效应,快衰落,慢衰落

多径效应 信号从发射端到接收端常有许多时延不同、损耗各异的传输路径&#xff0c;可以是直射、反射或是绕射。无线电波的多径效应是指不同路径的相同信号在按收端叠加会增大或减小接收信号能量的现象。 时间色散 在无线通信中&#xff0c;无线电波从发射端到接收端会经过直射、…

四旋翼

更新2018/11 1系统设计 硬件总体框架&#xff08;认识四旋翼的基本组成&#xff09; 机架主要的两种&#xff08;x型控制更难&#xff0c;动作灵活&#xff09; 十字型,X字型 桨 7040,8045&#xff08;常用&#xff09;&#xff0c;前四位直径&#xff0c;后四位角度 正反螺旋…

hotspot线程模型_Linux上的HotSpot GC线程CPU占用空间

hotspot线程模型以下问题将测试您对Linux操作系统上运行的Java应用程序的垃圾收集和高CPU故障排除的知识。 当调查过多的GC和/或CPU利用率时&#xff0c;此故障排除技术尤其重要。 它将假定您无权使用高级监控工具&#xff0c;例如Compuware dynaTrace甚至JVisualVM。 将来将介…

数字信号处理实验一

补充2019/10/26 (1)序列的加、减、乘、除和乘方运算。输入A[1 2 3 4]&#xff0c;B[3 4 5 6]&#xff0c;起点n0&#xff0c;求CAB&#xff0c;DA-B&#xff0c;EA.*B&#xff0c;FA./B&#xff0c;GA.^B&#xff0c;并用stem语句画出A&#xff0c;B&#xff0c;C&#xff0c;D…

SRP是骗局

根据罗伯特马丁 &#xff08; Robert Martin&#xff09;的 《 清洁法 》&#xff08; Clean Code&#xff09; &#xff0c;“ 单一责任原则 ”意味着“一个阶级应该只有一个改变的理由”。 让我们尝试解密这个模糊的语句&#xff0c;看看它如何帮助我们设计更好的面向对象软件…

数字信号处理实验二

补2019/11/2 &#xff08;1&#xff09;编写函数如xsiAdd(x1,n1,x2,n2),实现两个序列x1和x2的相加&#xff0c;其中x1的下标为n1n1s:nlf&#xff0c;x2的下标为n2n2s:n2f。绘制出该函数的图形。设x1&#xff08;n&#xff09;n,0≦n1≦9,x2(n)n,4≦n2≦15。 &#xff08;2&…