2017-10-21 4 views
12

Bisher bis nicht-modularisierten Java-Zugriff, würden Sie einfach eine Datei in src/main/java/resources setzen sicherstellen, dass es in Classpath ist und es dann laden mitJava 9 Puzzle - Ressourcendateien von externen Modulen

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

von ziemlich irgendwo im Klassenpfad.

Jetzt mit Modulen verdickt sich die Handlung.

Mein Projekt-Setup ist die folgende:

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

Config-Datei in src/main/resources/config.yml platziert wird.

Projekt ausgeführt wird mit

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

Da die Hauptklasse nicht in meinem eigenen Projekt befindet, sondern in einem externen Rahmen-Modul, kann es nicht config.yml sehen. Jetzt ist die Frage, gibt es eine Möglichkeit, meine Config-Datei irgendwie in das Modul zu setzen oder es zu öffnen? Muss ich die Art ändern, in der die Datei vom Framework-Upstream geladen wird?

Ich habe versucht, "Exporte" oder "öffnet" in Modul-Info, aber es möchte einen Paketnamen, nicht einen Ordnernamen.

Wie erreicht man dies in der besten praktischen Weise, so dass es wie in Java 8 und mit so wenig Änderungen wie möglich funktionieren würde?

+3

Hat 'com.framework.Main' Ressourcen lesen mit' Class.getResource'? – nullpointer

+2

Wenn Code in einem Modul auf eine seiner eigenen Ressourcen zugreifen muss, sollten die Class.getResourceXXX-Methoden verwendet werden (der Parametername ist ein Ressourcenname, kein Dateiname btw). Wenn sich die Ressource in einem anderen Modul befindet und Sie das Module-Objekt haben, können Sie Module.getResourceAsStream verwenden. Wenn Sie den Modulpfad und den Klassenpfad durchsuchen möchten, funktioniert ClassLoader.getResourceXXX wie zuvor, aber das Modul muss das Paket öffnen, das die Ressource enthält. Ressourcen im obersten Verzeichnis oder META-INF/* sind nicht gekapselt, daher funktioniert ClassLoader.getResource. –

+2

In Verbindung stehendes [how-to-let-ein-automatisch-Modul-finden-es-eigenen-Ressourcen-in-java-9] (https://stackoverflow.com/questions/43573809/how-to-let-an- Automatische Modul-Suche-its-eigenen-Ressourcen-in-Java-9) – nullpointer

Antwort

4

Während Sie den java Befehl verwenden, eine Anwendung zu starten, wie folgt: -

java -p target/classes:target/dependency -m framework.core/com.framework.Main 
  • Sie unter Angabe der ModulePath die Option -p aternate für --module-path verwendet, die in Ziel/Klassen aussehen würde und Ziel/Abhängigkeit für Ihre Module.

  • Daneben Verwendung -m Stellvertreter für --module gibt den Anfangsmodul mit dem Namen framework.core und baut das Modul Graph mit der Hauptklasse zu lösen explizit als com.framework.Main aufgeführt auszuführen.

Nun, das Problem scheint hier zu sein, dass das Modul framework.core nicht requires oder playground.api Modul gelesen, weil der das Modul Graph nicht das gewünschte Modul enthält, bestehend aus der tatsächlichen Ressource config.yml.

Wie suggested by @Alan, eine gute Möglichkeit, die Modulauflösung Ausgabe während des Starts aufzulisten, ist die Verwendung der Option --show-module-resolution.


Ich habe gerade versucht naiv src/main/Ressourcen öffnet, nicht kompiliert OFC

Da die Ressource in Ihrem Modul am root level ist, ist es daher not encapsulated und muss nicht zu einem anderen Modul geöffnet oder exportiert werden.

In Ihrem Fall müssen Sie nur sicherstellen, dass das Modul playground.api im Moduldiagramm landet und dann die Ressource für die Anwendung zugänglich wäre. Um Stammmodule anzugeben, die zusätzlich zum ursprünglichen Modul aufgelöst werden sollen, können Sie die Option --add-modules verwenden.


Daraus ergibt sich die Gesamtlösung für Sie arbeiten zusammen mit einigen Debuggen sein soll:

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) 

Siehe ein Arbeitsbeispiel unten.


Gegeben:

. 
├── 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); 
    } 
} 

sein könnte Und Sie würden starten mit

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

dieses Beispiel zu klonen: git clone https://github.com/j4n0/SO-46861589.git

+1

Siehe [App.java:20](https://github.com/j4n0/SO-46861589/blob/master/PlaygroundApi/src/PlaygroundApi/com/playground/api/App.java#L20] um eine Ressource zu laden aus dem gleichen Modul. – Jano

+0

Ich werde es versuchen, nur um zu sehen, ob es funktioniert, aber ich habe ein Problem damit. Im Idealfall sollte sich das Framework-Modul nicht darum kümmern, welches Modul die Ressourcendatei enthält, noch sollte es sich darum kümmern, dass das betreffende Modul "PlaygroundApi" genannt wird. Wenn ich 100 Projekte mache, hat jeder einen anderen Modulnamen, was bedeutet, dass ich den Modulnamen im Rahmenprogramm konfigurierbar machen muss. Irgendwie schmerzhaft, aber machbar, denke ich. – cen

+1

Die Antwort ist ein bisschen irreführend, da dort das Framework nur die Modul-API verwenden soll, dann will es eine Ressource in einem bestimmten Modul finden. Wie in einem der frühen Kommentare angemerkt, wird eine Ressource nur gekapselt, wenn sie sich in einem Modulpaket befindet. In diesem Fall kann module-info.java das Paket öffnen, damit das Framework die Ressource lokalisieren kann. –

Verwandte Themen