dchaser wordpress,企业网站优化分为两个方向,服务器查询,在下列软件中微服务java模块内存管理接线与查找 Java长期以来都有一个ServiceLoader类。 它是在1.6中引入的#xff0c;但是自Java 1.2以来就使用了类似的技术。 一些软件组件使用了它#xff0c;但是使用并不广泛。 它可以用于模块化应用程序#xff08;甚至更多#xff09;#xff0… 微服务java模块内存管理 接线与查找 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不在乎。 它不假定实际的类在文件中。 有很多应用程序可以从文件而不是文件中加载类。 实际上大多数应用程序从不同的媒体加载某些类。 还有您的程序您可能不知道。 您曾经使用过SpringHibernate或其他框架吗 这些框架大多数都在运行时创建代理对象并使用特殊的类加载器从内存中加载这些对象。 类加载器无法告诉您它所支持的框架是否会创建一个新对象。 在这种情况下类路径不是静态的。 这些特殊类加载器甚至没有类路径之类的东西。 他们动态地找到类。 好的。 说得好并详细介绍。 但是再说一遍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解决方案的同时引入了一种新方法。 随着Jigsaw的引入我们有了模块而模块有了模块描述符。 模块可以定义ServiceLoader可以加载的服务模块还可以指定可能需要通过ServiceLoader加载哪些服务。 发现服务接口实现的这种新方式从文本资源转移到Java代码。 它的纯粹优点是可以在编译期间或模块加载时间识别与错误名称相关的编码错误以使失败的代码更快地失败。 为了使事情变得更加灵活或者只是使它们变得无用的变得更加复杂将来会告诉人们如果该类不是服务接口的实现但确实具有返回该类实例的public static provider()方法则Java 9也可以使用实现该接口。 顺便说一句在这种情况下提供程序类甚至可以根据需要实现服务接口但是通常它是工厂所以为什么要这么做。请注意SRP。 样例代码 您可以从https://github.com/verhas/module-test下载多模块Maven项目。 该项目包含三个模块Consumer Provider和ServiceInterface 。 使用者调用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微服务java模块内存管理