Arquivo dentro do jar não é visível para a primavera

103

Tudo

Criei um arquivo jar com o seguinte MANIFEST.MF dentro:

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

Em sua raiz, há um arquivo chamado my.config que é referenciado em my spring-context.xml como este:

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

Se eu executar o jar, tudo parecerá bem, exceto o carregamento daquele arquivo específico:

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
  • as classes são carregadas de dentro do jar
  • spring e outras dependências são carregadas de jars separados
  • o contexto do spring é carregado (new ClassPathXmlApplicationContext ("spring-context / applicationContext.xml"))
  • my.properties é carregado em PropertyPlaceholderConfigurer ("classpath: my.properties")
  • se eu colocar meu arquivo .config fora do sistema de arquivos e alterar o url do recurso para 'arquivo:', tudo parece estar bem ...

Alguma dica?

BTakacs
fonte

Respostas:

211

Se seus arquivos spring-context.xml e my.config estiverem em jars diferentes, você precisará usar classpath*:my.config?

Mais informações aqui

Além disso, certifique-se de que resource.getInputStream()não está usando resource.getFile()ao carregar de dentro de um arquivo jar.

sbk
fonte
1
Eles estão no mesmo jar, mas tentei sua solução com o mesmo resultado: java.io.FileNotFoundException: class path resource [classpath *: my.config] não pode ser resolvido para URL porque ele não existe
BTakacs
14
Olhando novamente, parte do seu código de chamada (possivelmente BeanConfigurationFactoryBean) está tentando carregar um java.io.File. Arquivo refere-se a arquivos no sistema de arquivos, o que as entradas em um jar não são. O código de chamada deve usar resource.getInputStream em vez de carregar de um jar.
sbk
57
... e ESTA é a resposta ... Obrigado! Dentro de um jar, não use resource.getFile () :-)
BTakacs
2
alguma chance de haver um "por quê?" atrás de não usar getFile () dentro de um jar? É simplesmente que o arquivo está dentro do Jar e, portanto, o "arquivo" é o arquivo jar ??
RockMeetHardplace
8
É isso aí. Um java.io.File representa um arquivo no sistema de arquivos, em uma estrutura de diretório. O Jar é um java.io.File. Mas qualquer coisa dentro desse arquivo está além do alcance de java.io.File. No que diz respeito ao java, até que seja descompactado, uma classe em um arquivo jar não é diferente de uma palavra em um documento do Word.
sbk
50

Eu sei que esta pergunta já foi respondida. No entanto, para aqueles que usam o spring boot, este link me ajudou - https://smarterco.de/java-load-file-classpath-spring-boot/

No entanto, o resourceLoader.getResource("classpath:file.txt").getFile();estava causando esse problema e o comentário do sbk:

É isso aí. Um java.io.File representa um arquivo no sistema de arquivos, em uma estrutura de diretório. O Jar é um java.io.File. Mas qualquer coisa dentro desse arquivo está além do alcance de java.io.File. No que diz respeito ao java, até que seja descompactado, uma classe em um arquivo jar não é diferente de uma palavra em um documento do Word.

me ajudou a entender por que usar em seu getInputStream()lugar. Trabalha para mim agora!

Obrigado!

Jmathewt
fonte
37

No pacote do pote de primavera, uso new ClassPathResource(filename).getFile(), que lança a exceção:

não pode ser resolvido para o caminho de arquivo absoluto porque ele não reside no sistema de arquivos: jar

Mas usar novo ClassPathResource(filename).getInputStream()resolverá esse problema. A razão é que o arquivo de configuração no jar não existe na árvore de arquivos do sistema operacional, então deve ser usado getInputStream().

tao zeng
fonte
2

Tive um problema semelhante ao usar o Tomcat6.x e nenhum dos conselhos que encontrei estava ajudando. No final apaguei a workpasta (do Tomcat) e o problema foi embora.

Eu sei que é ilógico, mas para fins de documentação ...

takacsot
fonte
1

A resposta de @sbk é a forma como devemos fazer isso no ambiente de boot do spring (além de @Value ("$ {classpath *:})), na minha opinião. Mas no meu cenário não estava funcionando se executarmos de forma autônoma jar ... pode ser que eu fiz algo errado.

Mas esta pode ser outra maneira de fazer isso,

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

Eu estava tendo um problema mais complexo porque tenho mais de um arquivo com o mesmo nome, um está no jar principal do Spring Boot e os outros estão nos jarros dentro do jar principal de gordura. Minha solução foi conseguir todos os recursos com o mesmo nome e depois pegar aquele que eu precisava filtrar por nome de pacote. Para obter todos os arquivos:

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

Eu estava tendo um problema ao carregar recursos recursivamente em meu aplicativo Spring e descobri que o problema era que eu deveria estar usando resource.getInputStream. Aqui está um exemplo mostrando como ler de forma recursiva em todos os arquivos em config/myfilesque são jsonarquivos.

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
fonte
0

Eu tive o mesmo problema, acabei usando os Recursos Guava muito mais convenientes :

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