4

我又遇到了一個奇怪的問題,希望這裏有人能夠提供幫助。使用可執行Jar時未找到彈簧引導資源

我有一個彈簧啓動後端模塊,在eclipse中可以正常工作,並且在application.java中啓動main時可以執行應用程序。一切都很好。

我的應用程序使用csv-files將示例數據導入數據庫src/main/resources文件夾中包含的內容。如前所述,在eclipse中啓動一切正常。

現在我想執行它作爲可執行jar,應用程序開始啓動,然後啓動失敗,因爲它無法找到csv文件。打印出來的路徑以及查找文件的位置是正確的,並且csv文件包含在jar中。看起來

模塊的雙龍一樣如下:

<project> 
    <modelVersion>4.0.0</modelVersion> 

    <parent> 
     <groupId>at.company.bbsng</groupId> 
     <artifactId>bbsng-import</artifactId> 
     <version>0.1.0-SNAPSHOT</version> 
    </parent> 

    <artifactId>bbsng-import-backend</artifactId> 
    <name>bbsng-import-backend</name> 

    <properties> 
     <start-class>at.company.bbsng.dataimport.Application</start-class> 
    </properties> 


    <dependencies> 

     <!-- SPRING ... --> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-batch</artifactId> 
      <!-- EXCLUDE LOGBACK AND USE LOG4J --> 
      <exclusions> 
       <exclusion> 
        <artifactId>spring-boot-starter-logging</artifactId> 
        <groupId>org.springframework.boot</groupId> 
       </exclusion> 
      </exclusions> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-log4j</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-test</artifactId> 
      <scope>test</scope> 
     </dependency> 

     <!-- COMMONS ... --> 

     ... 

    </dependencies> 

    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.springframework.boot</groupId> 
       <artifactId>spring-boot-maven-plugin</artifactId> 
       <executions> 
        <execution> 
         <goals> 
          <goal>repackage</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 
     </plugins> 
    </build> 

</project> 

路徑的CSV文件中屬性格式文件配置如下:

# EXAMPLE PATH 
csv.path=config/csv/ 

Java的配置文件的部分如下:

... 

    @Value("${csv.path}") 
    private String csvExamplePath; 

    @Bean 
    public Resource addressResource() { 
    return new ClassPathResource(csvExamplePath + CSV_ADDRESS); 
    } 

    ... 

在JAR文件位於路徑

\config\csv\ 

堆棧跟蹤:

Caused by: java.io.FileNotFoundException: class path resource [config/csv/Company.csv] cannot be resolved to absolute file path because it does not reside in th 
e file system: jar:file:/C:/Development/Projekte/bbsng/trunk/import/backend/target/bbsng-import-backend-0.1.0-SNAPSHOT.jar!/config/csv/Company.csv 
     at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:207) 
     at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:52) 
     at at.compax.bbsng.dataimport.app.source.company.CompanyGenerator.init(CompanyGenerator.java:28) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
     at java.lang.reflect.Method.invoke(Unknown Source) 
     at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java 

此外,應用程序的工作,從日食開始時,它如預期,僅可執行的JAR抱怨缺少的CSV文件,什麼都在罐子了。

任何線索都會很棒。

回答

13

好的,我已經找到了真正的問題和解決方案。

首先,應用程序使用csv文件的正確路徑,但使用可執行jar時出現的另一個問題是我在下面的鏈接下找到的。 Stackoverflow-Link

之前我跟可執行的JAR我用於獲取的CSV文件以下解決方案(問題的GetFile())發行:

final List<String> resourceLines = FileReadUtils.readLines(specialisationResource.getFile()); 
for (final String line : resourceLines) { 
    data.add(getNewTransientSpecialisation(line)); 
} 

但executeable罐子我不能用我的資源作爲文件,我需要使用它作爲流,請參閱上面提供的鏈接。所以我需要改變我的代碼。如果你更喜歡使用本地Java,你可以做如下:

final InputStream inputStream = specialisationResource.getInputStream(); 
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); 

String line; 
while ((line = bufferedReader.readLine()) != null) { 
    data.add(getNewTransientSpecialisation(line)); 
} 

我更喜歡使用框架,並使用Apache的百科全書一樣如下:

final List<String> resourceLines = IOUtils.readLines(specialisationResource.getInputStream()); 
for (final String line : resourceLines) { 
    data.add(getNewTransientSpecialisation(line)); 
} 

所以只要記住,不要爲獲得使用文件()資源,始終使用流從開始避免該問題:-)

希望可以幫助別人。

+0

可否請你看看這個,我有類似的問題http://stackoverflow.com/questions/37713335/resource-filenotfoundexception-when-using-executable-jar-command -with-spring-boo – 2016-06-08 22:05:53

2

我遇到了這個限制,也並創造了這個庫,以解決該問題:spring-boot-jar-resources

基本上,它允許您註冊使用Spring引導定製的ResourceLoader,由於需要從JAR中提取的classpath資源,透明:

new SpringApplicationBuilder() 
     .sources(Application.class) 
     .resourceLoader(new JarResourceLoader()) 
     .run(args); 

使用該ResourceLoader,您可以在任何類路徑資源上執行resource.getFile()

+0

感謝您的分享,聽起來不錯:-) – 2016-05-13 19:10:32

+1

我希望有一種方法可以在本地執行此操作,而無需依賴另一個外部庫。 – Lucas 2016-09-12 22:16:44

0

現在我需要從JAR中找到資源文件夾中的xmlFiles,並且面臨着類似的問題。我想分享我的發現,以及我如何得到它的工作,也許這是有幫助的。

在一個罐子裏,我在src/main/resources下有一個「db-input」文件夾,其中包含任何用於DB-Input的xml文件的編號。我的應用程序是基於Spring:

@Component 
public class DatabaseInitializer implements InitializingBean { 
    @Autowired DomainConfigurationRepository repository; 
    @Autowired MarshallerService marshallerService; 
    @Autowired ApplicationContext context; 

    @Override 
    public void afterPropertiesSet() throws Exception { 
     final Resource[] resources = context.getResources("classpath*:db-input/*"); 
     final Set<String> filePaths = findInputFileNames(resources); 
     final Set<DomainConfiguration> configurations = createConfigurations(filePaths); 
     repository.save(configurations); 
    } 

    private Set<DomainConfiguration> createConfigurations(final Set<String> filePaths) throws Exception { 
     final Set<DomainConfiguration> configurations = new HashSet<>(); 
     for(final String path : filePaths){ 
      final Resource resource = context.getResource(path); 
      final DomainConfigurationXO xoConfiguration = marshallerService.unmarshal(resource.getInputStream()); 
      final DomainConfiguration configuration = PopulationUtils.getPopulatedConfiguration(xoConfiguration); 
      configurations.add(configuration); 
     } 
     return configurations; 
    } 

    public Set<String> findInputFileNames(final Resource[] inputDirectoryResources) throws IOException { 
     return Arrays.stream(inputDirectoryResources) 
       .map(resource -> extractURI(resource)) 
       .collect(Collectors.toSet()); 
    } 

    private String extractURI(Resource resource){ 
     try { 
      return resource.getURI().toString(); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
    } 
}