Die Feile im Glas ist für den Frühling nicht sichtbar

103

Alles

Ich habe eine JAR-Datei mit der folgenden MANIFEST.MF erstellt:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.6.0_25-b06 (Sun Microsystems Inc.)
Main-Class: my.Main
Class-Path: . lib/spring-core-3.2.0.M2.jar lib/spring-beans-3.2.0.M2.jar

Im Stammverzeichnis befindet sich eine Datei namens my.config, auf die in meiner spring-context.xml wie folgt verwiesen wird:

<bean id="..." class="...">
    <property name="resource" value="classpath:my.config" />
</bean>

Wenn ich das Glas starte, sieht alles gut aus, außer beim Laden dieser bestimmten Datei:

Caused by: java.io.FileNotFoundException: class path resource [my.config] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/D:/work/my.jar!/my.config
        at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:205)
    at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:52)
    at eu.stepman.server.configuration.BeanConfigurationFactoryBean.getObject(BeanConfigurationFactoryBean.java:32)
    at eu.stepman.server.configuration.BeanConfigurationFactoryBean.getObject(BeanConfigurationFactoryBean.java:1)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
    ... 22 more
  • Klassen werden aus dem Glas geladen
  • Feder und andere Abhängigkeiten werden aus getrennten Gläsern geladen
  • Der Frühlingskontext wird geladen (neuer ClassPathXmlApplicationContext ("spring-context / applicationContext.xml"))
  • my.properties wird in PropertyPlaceholderConfigurer geladen ("Klassenpfad: my.properties")
  • Wenn ich meine .config-Datei außerhalb des Dateisystems ablege und die Ressourcen-URL in 'file:' ändere, scheint alles in Ordnung zu sein ...

Irgendwelche Tipps?

BTakacs
quelle

Antworten:

209

Wenn sich Ihre Dateien spring-context.xml und my.config in verschiedenen Gläsern befinden, müssen Sie classpath*:my.config?

Mehr Infos hier

Stellen Sie außerdem sicher, dass Sie resource.getInputStream()nicht verwenden, resource.getFile()wenn Sie aus einer JAR-Datei laden.

sbk
quelle
1
Sie befinden sich im selben JAR, aber ich habe Ihre Lösung mit demselben Ergebnis ausprobiert: java.io.FileNotFoundException: Klassenpfadressource [Klassenpfad *: my.config] kann nicht in URL aufgelöst werden, da sie nicht vorhanden ist
BTakacs
14
Bei erneuter Betrachtung versucht ein Teil Ihres aufrufenden Codes (möglicherweise BeanConfigurationFactoryBean), eine java.io.File zu laden. Datei bezieht sich auf Dateien im Dateisystem, die Einträge in einem JAR nicht sind. Der aufrufende Code sollte stattdessen resource.getInputStream verwenden, um aus einem JAR zu laden.
sbk
57
... und DAS ist die Antwort ... Danke! Verwenden Sie in einem Glas nicht resource.getFile () :-)
BTakacs
2
Gibt es eine Chance, dass es ein "Warum" gibt? dahinter getFile () nicht in einem Glas verwenden? Ist es einfach so, dass sich die Datei im Jar befindet und daher die "Datei" die JAR-Datei ist?
RockMeetHardplace
8
Das ist es. Eine java.io.File repräsentiert eine Datei im Dateisystem in einer Verzeichnisstruktur. Das Glas ist eine java.io.File. Aber alles in dieser Datei ist außerhalb der Reichweite von java.io.File. In Bezug auf Java unterscheidet sich eine Klasse in einer JAR-Datei nicht von einem Wort in einem Word-Dokument, bis sie dekomprimiert wird.
sbk
50

Ich weiß, dass diese Frage bereits beantwortet wurde. Für diejenigen, die Spring Boot verwenden, hat mir dieser Link jedoch geholfen - https://smarterco.de/java-load-file-classpath-spring-boot/

Dies resourceLoader.getResource("classpath:file.txt").getFile();verursachte jedoch dieses Problem und den Kommentar von sbk:

Das ist es. Eine java.io.File repräsentiert eine Datei im Dateisystem in einer Verzeichnisstruktur. Das Glas ist eine java.io.File. Aber alles in dieser Datei ist außerhalb der Reichweite von java.io.File. In Bezug auf Java unterscheidet sich eine Klasse in einer JAR-Datei nicht von einem Wort in einem Word-Dokument, bis es dekomprimiert wird.

half mir zu verstehen, warum ich getInputStream()stattdessen verwenden sollte. Es funktioniert jetzt für mich!

Vielen Dank!

jmathewt
quelle
37

Im Spring Jar Package verwende ich new ClassPathResource(filename).getFile(), was die Ausnahme auslöst:

kann nicht in einen absoluten Dateipfad aufgelöst werden, da er sich nicht im Dateisystem befindet: jar

Die Verwendung von new ClassPathResource(filename).getInputStream()löst dieses Problem. Der Grund dafür ist, dass die Konfigurationsdatei im JAR nicht im Dateibaum des Betriebssystems vorhanden ist und daher verwendet werden muss getInputStream().

tao zeng
quelle
2

Ich hatte ein ähnliches Problem bei der Verwendung von Tomcat6.x und keiner der Ratschläge, die ich fand, half. Am Ende habe ich gelöschtwork Ordner (von Tomcat) und das Problem ist verschwunden.

Ich weiß, dass es unlogisch ist, aber zu Dokumentationszwecken ...

Takacsot
quelle
1

Die Antwort von @sbk ist meiner Meinung nach die Art und Weise, wie wir dies in einer Spring-Boot-Umgebung tun sollten (abgesehen von @Value ("$ {classpath *:})). In meinem Szenario funktionierte es jedoch nicht, wenn die Ausführung von Standalone aus erfolgte jar..kann sein, dass ich etwas falsch gemacht habe.

Dies kann aber auch ein anderer Weg sein,

InputStream is = this.getClass().getClassLoader().getResourceAsStream(<relative path of the resource from resource directory>);
Abhishek Chatterjee
quelle
1

Ich hatte ein komplexeres Problem, weil ich mehr als eine Datei mit demselben Namen habe, eine im Hauptglas von Spring Boot und andere in Gläsern im Hauptglas von Fat. Meine Lösung bestand darin, alle Ressourcen mit demselben Namen abzurufen und danach die zu erhalten, die ich nach Paketnamen filtern musste. So erhalten Sie alle Dateien:

ResourceLoader resourceLoader = new FileSystemResourceLoader();
final Enumeration<URL> systemResources = resourceLoader.getClassLoader().getResources(fileNameWithoutExt + FILE_EXT);
Enrique Jiménez Flores
quelle
0

Ich hatte ein Problem beim rekursiven Laden von Ressourcen in meiner Spring-App und stellte fest, dass das Problem darin bestand, dass ich es verwenden sollte resource.getInputStream. Hier ist ein Beispiel, das zeigt, wie alle Dateien in config/myfilesdiesen jsonDateien rekursiv eingelesen werden .

Beispiel.java

private String myFilesResourceUrl = "config/myfiles/**/";
private String myFilesResourceExtension = "json";

ResourceLoader rl = new ResourceLoader();

// Recursively get resources that match. 
// Big note: If you decide to iterate over these, 
// use resource.GetResourceAsStream to load the contents
// or use the `readFileResource` of the ResourceLoader class.
Resource[] resources = rl.getResourcesInResourceFolder(myFilesResourceUrl, myFilesResourceExtension);

// Recursively get resource and their contents that match. 
// This loads all the files into memory, so maybe use the same approach 
// as this method, if need be.
Map<Resource,String> contents = rl.getResourceContentsInResourceFolder(myFilesResourceUrl, myFilesResourceExtension);

ResourceLoader.java

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.StreamUtils;

public class ResourceLoader {
  public Resource[] getResourcesInResourceFolder(String folder, String extension) {
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    try {
      String resourceUrl = folder + "/*." + extension;
      Resource[] resources = resolver.getResources(resourceUrl);
      return resources;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public String readResource(Resource resource) throws IOException {
    try (InputStream stream = resource.getInputStream()) {
      return StreamUtils.copyToString(stream, Charset.defaultCharset());
    }
  }

  public Map<Resource, String> getResourceContentsInResourceFolder(
      String folder, String extension) {
    Resource[] resources = getResourcesInResourceFolder(folder, extension);

    HashMap<Resource, String> result = new HashMap<>();
    for (var resource : resources) {
      try {
        String contents = readResource(resource);
        result.put(resource, contents);
      } catch (IOException e) {
        throw new RuntimeException("Could not load resource=" + resource + ", e=" + e);
      }
    }
    return result;
  }
}
Brad Parks
quelle
0

Ich hatte das gleiche Problem und nutzte die viel bequemeren Guava-Ressourcen :

Resources.getResource("my.file")
Ahmad Abdelghany
quelle