2017-10-21 125 views
12

資源文件到目前爲止,直到非模塊化Java中,你會簡單地把一個文件src/main/java/resources確保它是在類路徑中,然後從幾乎與的Java 9個拼圖 - 訪問來自外部模塊

file = getClass().getClassLoader().getResourceAsStream("myfilename"); 

加載在類路徑的任何地方。

現在使用模塊,情節變厚。

我的項目設置如下:

module playground.api { 
    requires java.base; 
    requires java.logging; 
    requires framework.core; 
} 

配置文件放在裏面src/main/resources/config.yml

項目與

java -p target/classes:target/dependency -m framework.core/com.framework.Main 

運行,因爲主類不駐留在我自己的項目,但外部框架模塊,它不能看到config.yml。現在的問題是,有沒有辦法以某種方式將我的配置文件放入模塊或打開它?我必須改變上游框架加載文件的方式嗎?

我試過在模塊信息中使用「輸出」或「打開」,但它想要一個包名稱,而不是文件夾名稱。

如何以最實用的方式實現這一目標,以便它能像Java 8一樣工作,並儘可能地進行少許更改?

+3

是否'com.framework.Main'讀取使用'Class.getResource'資源? – nullpointer

+2

如果模塊中的代碼需要訪問自己的某個資源,則應該使用Class.getResourceXXX方法(參數名稱是資源名稱,而不是文件名稱btw)。如果資源位於另一個模塊中,並且具有Module對象,則可以使用Module.getResourceAsStream。如果您想搜索模塊路徑和類路徑,那麼ClassLoader.getResourceXXX將像以前一樣工作,但模塊需要打開包含該資源的包。最頂層目錄或META-INF/*中的資源沒有被封裝,所以ClassLoader.getResource將會工作。 –

+2

相關[如何讓一個自動模塊找到它自己的資源在java-9](https://stackoverflow.com/questions/43573809/how-to-let-an-自動模塊找到它自己的資源在java-9) – nullpointer

回答

4

當您使用java命令,如下所示啓動一個應用程序: -

java -p target/classes:target/dependency -m framework.core/com.framework.Main 
  • 您正在使用的選項-p aternate爲--module-path這將仰望目標/類指定ModulePath進行目標/依賴關係爲您的模塊。

  • 除了使用-m替代爲--module指定初始模塊具有名稱framework.core解決並且構建與要執行明確列出作爲com.framework.Main主類的模塊圖。現在

,這裏的問題似乎是,該模塊framework.corerequires或閱讀playground.api模塊,因爲它的模塊圖不包括由實際資源config.yml的所需模塊。

作爲suggested by @Alan,在啓動期間列出模塊分辨率輸出的一個好方法是使用--show-module-resolution選項。


我只是天真地試圖打開的src/main /資源,不編譯OFC

由於您的模塊中的資源是在root level,它是,因此,not encapsulated,不需要打開或導出到任何其他模塊。

就你而言,你只需要確保模塊playground.api最終在模塊圖中,然後應用程序就可以訪問該資源。除了初始模塊之外,要指定要解析的根模塊,可以使用--add-modules選項。


因此,整體解決方案,爲你工作的一些調試沿應爲:

java --module-path target/classes:target/dependency 
    --module framework.core/com.framework.Main 
    --add-modules playground.api 
    --show-module-resolution 
3
// to scan the module path 
ClassLoader.getSystemResources(resourceName) 

// if you know a class where the resource is 
Class.forName(className).getResourceAsStream(resourceName) 

// if you know the module containing the resource 
ModuleLayer.boot().findModule(moduleName).getResourceAsStream(resourceName) 

查看下面的工作示例。


考慮:

. 
├── FrameworkCore 
│ └── src 
│  └── FrameworkCore 
│   ├── com 
│   │ └── framework 
│   │  └── Main.java 
│   └── module-info.java 
└── PlaygroundApi 
    └── src 
     └── PlaygroundApi 
      ├── com 
      │ └── playground 
      │  └── api 
      │   └── App.java 
      ├── config.yml 
      └── module-info.java 

Main.java可能是

package com.framework; 

import java.io.*; 
import java.net.URL; 
import java.util.Optional; 
import java.util.stream.Collectors; 

public class Main { 
    public static void main(String[] args) 
    { 
     // load from anywhere in the modulepath 
     try { 
      URL url = ClassLoader.getSystemResources("config.yml").nextElement(); 
      InputStream is = url.openStream(); 
      Main.read(is); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 

     // load from the the module where a given class is 
     try { 
      InputStream is = Class.forName("com.playground.api.App").getResourceAsStream("/config.yml"); 
      Main.read(is); 
     } catch (ClassNotFoundException e) { 
      throw new RuntimeException(e); 
     } 

     // load from a specific module 
     Optional<Module> specificModule = ModuleLayer.boot().findModule("PlaygroundApi"); 
     specificModule.ifPresent(module -> { 
      try { 
       InputStream is = module.getResourceAsStream("config.yml"); 
       Main.read(is); 
      } catch (Exception e) { 
       throw new RuntimeException(e); 
      } 
     }); 
    } 

    private static void read(InputStream is) { 
     String s = new BufferedReader(new InputStreamReader(is)).lines().collect(Collectors.joining("\n")); 
     System.out.println("config.yml: " + s); 
    } 
} 

而且你將與

java --module-path ./FrameworkCore/target/classes:./PlaygroundApi/target/classes \ 
    --add-modules FrameworkCore,PlaygroundApi \ 
     com.framework.Main 

推出克隆這個例子:git clone https://github.com/j4n0/SO-46861589.git

+1

請參閱[App.java:20](https://github.com/j4n0/SO-46861589/blob/master/PlaygroundApi/src/PlaygroundApi/com/playground/api/App.java#L20)加載資源來自同一個模塊。 – Jano

+0

我會試試這個只是爲了看看它是否能正常工作,但我對此有疑問。理想情況下,框架模塊不應該關心哪個模塊包含資源文件,也不應該關心所討論的模塊被稱爲「PlaygroundApi」。如果我製作了100個項目,每個項目都有不同的模塊名稱,這意味着我需要在框架代碼中配置模塊名稱。有點痛苦但可行我猜。 – cen

+1

答案有點讓人誤解,因爲框架應該只需要使用Module API,然後它想要在特定模塊中找到資源。正如其中一個早期評論所注意的那樣,如果資源位於模塊包中,則資源僅被封裝,在這種情況下,module-info.java可以打開包以允許框架查找資源。 –