2017-10-21 123 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可以打开包以允许框架查找资源。 –