Java 9 jigsaw - доступ к файлам ресурсов из внешних модулей

До тех пор, пока не модулярная 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. Теперь вопрос в том, есть способ каким-то образом положить мой файл конфигурации в модуль или открыть его? Должен ли я изменить способ загрузки файла фреймворком вверх по течению?

Я попытался использовать "export" или "opens" в модуле info, но он хочет иметь имя пакета, а не имя папки.

Как добиться этого наилучшим образом, чтобы он работал как на Java 8 и с минимальными изменениями?

Ответ 1

Пока вы используете команду java для запуска приложения следующим образом: -

java -p target/classes:target/dependency -m framework.core/com.framework.Main 
  • вы указываете путь к модулю с помощью опции -p aternate для --module-path, которая будет искать в целевых/классов и цели/зависимости для ваших модулей.

  • Кроме того, использование -m alternate для --module указывает начальный модуль для разрешения с именем framework.core и строит граф модуля с основным классом, чтобы выполнить явно перечисленные как com.framework.Main.

Теперь проблема заключается в том, что модуль framework.core не имеет requires или читает модуль playground.api, из-за которого графа модуля не включает желаемый модуль, состоящий из фактического ресурса config.yml.

Как предложенный @Alan, хороший способ отобразить вывод разрешения модуля во время запуска состоит в использовании опции --show-module-resolution.


Я просто наивно пытался открыть src/main/resources, не компилирует ofc

Поскольку ресурс в вашем модуле находится на уровне root, поэтому он и его не нужно открывать или экспортировать в какой-либо другой модуль.

В вашем случае вам просто нужно убедиться, что модуль playground.api заканчивается в графе модулей, а затем ресурс будет доступен для приложения. Чтобы указать корневые модули для решения в дополнение к исходному модулю, вы можете использовать опцию --add-modules.


Следовательно, общее решение для работы с вами вместе с некоторой отладкой должно быть:

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

Ответ 2

// 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