微服务java模块内存管理_Java 9模块服务
微服務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不在乎。 它不假定實際的類在文件中。 有很多應用程序可以從文件而不是文件中加載類。 實際上,大多數應用程序從不同的媒體加載某些類。 還有您的程序,您可能不知道。 您曾經使用過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解決方案的同時引入了一種新方法。 隨著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 ,并且它也不需要模塊導出任何包。
像啟動程序一樣的命令是
它可以在成功執行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模塊內存管理
總結
以上是生活随笔為你收集整理的微服务java模块内存管理_Java 9模块服务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 很多人不知道淘汰的旧电脑要怎么处理淘汰旧
- 下一篇: 华为推出Mate 60 RS非凡大师手机