Pilnik w słoiku nie jest widoczny na wiosnę

104

Wszystko

Utworzyłem plik jar z następującym plikiem MANIFEST.MF wewnątrz:

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

W jego katalogu głównym znajduje się plik o nazwie my.config, do którego odwołuje się mój spring-context.xml w następujący sposób:

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

Jeśli uruchomię jar, wszystko wygląda dobrze, z wyjątkiem załadowania tego konkretnego pliku:

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
  • klasy są ładowane z wnętrza jar
  • sprężyna i inne zależności są ładowane z oddzielnych słoików
  • ładowany jest kontekst sprężynowy (new ClassPathXmlApplicationContext ("spring-context / applicationContext.xml"))
  • my.properties są ładowane do PropertyPlaceholderConfigurer („ścieżka klasy: my.properties”)
  • jeśli umieszczę plik .config poza systemem plików i zmienię adres URL zasobu na „plik:”, wszystko wydaje się być w porządku ...

Jakieś wskazówki?

BTakacs
źródło

Odpowiedzi:

211

Jeśli twoje pliki spring-context.xml i my.config znajdują się w różnych słoikach, będziesz musiał użyć classpath*:my.config?

Więcej informacji tutaj

Ponadto, upewnij się, że używasz resource.getInputStream()nie resource.getFile()podczas ładowania od wewnątrz pliku jar.

sbk
źródło
1
Znajdują się w tym samym słoiku, ale wypróbowałem Twoje rozwiązanie z tym samym wynikiem: java.io.FileNotFoundException: class path zasób [classpath *: my.config] nie może zostać zamieniony na URL, ponieważ nie istnieje
BTakacs
14
Patrząc ponownie, część Twojego kodu wywołującego (prawdopodobnie BeanConfigurationFactoryBean) próbuje załadować plik java.io.File. Plik odnosi się do plików w systemie plików, których nie ma w pliku jar. Kod wywołujący powinien zamiast tego używać zasobu resource.getInputStream do ładowania z jar.
sbk
58
... i TO jest odpowiedź ... Dzięki! W słoiku nie używaj
metody
2
jest szansa, że ​​jest "dlaczego?" za nieużywaniem getFile () wewnątrz jar? Czy to po prostu to, że plik znajduje się w Jar, a zatem „plik” jest plikiem jar?
RockMeetHardplace
8
Otóż ​​to. Plik java.io.File reprezentuje plik w systemie plików w strukturze katalogów. Jar to plik java.io. Ale wszystko w tym pliku jest poza zasięgiem java.io.File. Jeśli chodzi o java, do momentu zdekompresowania klasa w pliku jar nie różni się niczym od słowa w dokumencie tekstowym.
sbk
50

Wiem, że na to pytanie już udzielono odpowiedzi. Jednak dla tych, którzy używają rozruchu wiosennego, ten link pomógł mi - https://smarterco.de/java-load-file-classpath-spring-boot/

Jednak resourceLoader.getResource("classpath:file.txt").getFile();przyczyną tego problemu i komentarz sbk:

Otóż ​​to. Plik java.io.File reprezentuje plik w systemie plików w strukturze katalogów. Jar to plik java.io. Ale wszystko w tym pliku jest poza zasięgiem java.io.File. Jeśli chodzi o java, do momentu zdekompresowania klasa w pliku jar nie różni się niczym od słowa w dokumencie tekstowym.

pomogło mi zrozumieć, dlaczego getInputStream()zamiast tego używać . Teraz działa na mnie!

Dzięki!

jmathewt
źródło
38

W wiosennym opakowaniu słoika używam nowego ClassPathResource(filename).getFile(), co rzuca wyjątek:

nie można przekształcić w bezwzględną ścieżkę do pliku, ponieważ nie znajduje się on w systemie plików: jar

Ale użycie nowego ClassPathResource(filename).getInputStream()rozwiąże ten problem. Powodem jest to, że plik konfiguracyjny w jar nie istnieje w drzewie plików systemu operacyjnego, więc należy go użyć getInputStream().

tao zeng
źródło
2

Miałem podobny problem podczas korzystania z Tomcat6.x i żadna ze znalezionych wskazówek nie pomogła. Na koniec skasowałemwork folder (Tomcat) i problem zniknął.

Wiem, że to nielogiczne, ale w celach dokumentacyjnych ...

takacsot
źródło
1

Odpowiedzią @sbk jest sposób, w jaki powinniśmy to zrobić w środowisku wiosennego rozruchu (oprócz @Value ("$ {classpath *:})), moim zdaniem. Ale w moim scenariuszu nie działało, jeśli wykonanie z samodzielnego jar… może zrobiłem coś złego.

Ale może to być inny sposób,

InputStream is = this.getClass().getClassLoader().getResourceAsStream(<relative path of the resource from resource directory>);
Abhishek Chatterjee
źródło
1

Miałem problem bardziej złożony, ponieważ mam więcej niż jeden plik o tej samej nazwie, jeden znajduje się w głównym słoiku Spring Boot, a inne w słoikach w głównym słoiku tłuszczu. Moim rozwiązaniem było pobranie wszystkich zasobów o tej samej nazwie, a następnie uzyskanie tego, którego potrzebowałem, i przefiltrowanie według nazwy pakietu. Aby pobrać wszystkie pliki:

ResourceLoader resourceLoader = new FileSystemResourceLoader();
final Enumeration<URL> systemResources = resourceLoader.getClassLoader().getResources(fileNameWithoutExt + FILE_EXT);
Enrique Jiménez Flores
źródło
0

Miałem problem z rekurencyjnym ładowaniem zasobów w mojej aplikacji Spring i stwierdziłem, że powinienem go używać resource.getInputStream. Oto przykład pokazujący, jak rekurencyjnie czytać wszystkie pliki w config/myfilestych jsonplikach.

Example.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
źródło
0

Miałem ten sam problem, w końcu skorzystałem z dużo wygodniejszych zasobów guawy :

Resources.getResource("my.file")
Ahmad Abdelghany
źródło