Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从外部模块访问资源文件_Java_Java 9_Java Platform Module System - Fatal编程技术网

Java 从外部模块访问资源文件

Java 从外部模块访问资源文件,java,java-9,java-platform-module-system,Java,Java 9,Java Platform Module System,到目前为止,在非模块化java之前,您只需将一个文件放入src/main/java/resources中,确保它位于类路径中,然后使用 file = getClass().getClassLoader().getResourceAsStream("myfilename"); 从类路径的几乎任何地方 现在有了模块,情节就变厚了 我的项目设置如下所示: module playground.api { requires java.base; requires java.loggin

到目前为止,在非模块化java之前,您只需将一个文件放入
src/main/java/resources
中,确保它位于类路径中,然后使用

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
。现在的问题是,有没有办法将我的配置文件放入模块或打开它?我是否必须更改框架上游加载文件的方式

我尝试在模块信息中使用“导出”或“打开”,但它希望有一个包名,而不是文件夹名

如何以最实际的方式实现这一点,使其与Java8一样工作,并尽可能少地进行更改

// 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);
    }
}
要克隆此示例:
git clonehttps://github.com/j4n0/SO-46861589.git

使用命令启动应用程序时,如下所示:-

java -p target/classes:target/dependency -m framework.core/com.framework.Main 
  • 您正在使用选项
    -p
    --module path
    指定modulepath,该选项将查找模块的目标/类和目标/依赖项

  • 此外,使用
    -m
    替代
    --module
    指定初始模块以
    framework.core
    的名称解析,并构建模块图,其中要执行的主类显式列示为
    com.framework.main

现在,这里的问题似乎是模块
framework.core
需要
或读取
playway.api
模块,因为模块图不包括由实际资源
config.yml
组成的所需模块

同样,在启动过程中列出模块分辨率输出的一个好方法是使用
--show module resolution
选项


我只是天真地试图打开src/main/resources,却没有编译ofc

由于模块中的资源位于,因此它是,不需要打开或导出到任何其他模块

在您的情况下,您只需要确保模块
playway.api
在模块图中结束,然后应用程序就可以访问该资源。要在初始模块之外指定要解析的根模块,可以使用
--addmodules
选项


因此,为您提供的总体解决方案以及一些调试应为:

java --module-path target/classes:target/dependency 
     --module framework.core/com.framework.Main
     --add-modules playground.api
     --show-module-resolution

在命名模块中运行时,
ClassLoader#getResource
具有非常令人惊讶的行为<代码>类加载器#getResourceAsStream
同样麻烦。我自己在将应用程序升级到命名模块时遇到了这个问题

如果您的代码位于命名模块中,并且您通过
ClassLoader\getResource
访问资源,必须无条件打开该资源的包。否则,即使资源位于同一模块中,您也无法检索该资源


这与
类#getResource
不同-请注意区别
Class#getResource
非常简单,没有这种令人讨厌的惊喜

关于
getResource
的所有内容也适用于
getResourceAsStream
方法

因此,我建议始终在
Class
上使用getResource方法,而不要在
ClassLoader
上使用getResource方法


另请参见我对

Does
com.framework.Main
read resources using
Class.getResource
?如果模块中的代码需要访问自己的资源之一,则应使用Class.getResourceXXX方法(参数名称是资源名称,而不是文件名)。如果资源位于另一个模块中,并且您拥有模块对象,则可以使用module.getResourceAsStream。如果要搜索模块路径和类路径,则ClassLoader.getResourceXXX将像以前一样工作,但模块需要打开包含资源的包。最顶层目录或META-INF/*中的资源未封装,因此ClassLoader.getResource将起作用。Related src/main/Resources/config.yml是src树中的文件路径,而不是资源名称。查看JAR文件,config.yml是否位于JAR文件的顶级目录中?如果是这样的话,那么框架将像以前一样使用ClassLoader.getResourceXXX来定位它。对,您不应该使用
-cp
将模块类添加到类路径中。您是否可以改为向命令行添加
——添加moduels playerd.api
。初始模块(您指定给-m的模块)是com.framework.Main,我假设没有人需要playery.api,因此它没有被解析(您可以通过添加
--show module resolution
来快速检查这一点,以在启动时跟踪解析)。请参阅从同一模块加载资源。我将尝试此操作,以查看它是否有效,我对此有意见。理想情况下,框架模块不应该关心哪个模块包含资源文件,也不应该关心所讨论的模块是否被称为“PlaygroundApi”。如果我做100个项目,每个项目都有不同的模块名,这意味着我需要在框架代码中配置模块名。有点痛苦,但我想是可行的。答案有点误导,因为框架应该只需要使用模块API,然后它想要在特定模块中定位资源。正如前面一条注释中所述,资源只有在模块包中时才会被封装,在这种情况下,module-info.java可以打开包以允许框架定位资源
java --module-path target/classes:target/dependency 
     --module framework.core/com.framework.Main
     --add-modules playground.api
     --show-module-resolution