Java 使用可执行 Jar 时找不到 Spring-Boot 资源
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26185137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring-Boot Resource Not Found when using executeable Jar
提问by Michael Hegner
again I face a strange issue and hope someone here can help.
我再次面临一个奇怪的问题,希望这里有人可以提供帮助。
I have a spring boot backend module, what works in eclipse well and application is executeable when starting main in application.java. Everything fine.
我有一个 spring boot 后端模块,在 eclipse 中运行良好,并且在 application.java 中启动 main 时应用程序是可执行的。一切都很好。
My application makes import of example data to database using csv-files what is included in src/main/resources folder. As mentioned, when starting in eclipse everything works.
我的应用程序使用 src/main/resources 文件夹中包含的 csv 文件将示例数据导入数据库。如前所述,在 eclipse 中启动时一切正常。
Now I would like to execute it as executable jar, the application begins to start and then it failed to start, because it cannot find the csv files. The path what it prints out, where it looked for the files, is correct and the csv files are in the jar included.
现在我想将它作为可执行 jar 执行,应用程序开始启动,然后启动失败,因为它找不到 csv 文件。它打印出的路径,它在何处查找文件,是正确的,并且 csv 文件包含在包含的 jar 中。
The Pom of the module looks like follows:
模块的 Pom 如下所示:
<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>
Path to csv-files are configured in propery files as follows:
csv 文件的路径在属性文件中配置如下:
# EXAMPLE PATH
csv.path=config/csv/
The part of java config file is as follows:
java配置文件部分如下:
...
@Value("${csv.path}")
private String csvExamplePath;
@Bean
public Resource addressResource() {
return new ClassPathResource(csvExamplePath + CSV_ADDRESS);
}
...
In the jar the files are located at path
在 jar 中,文件位于路径
\config\csv\
Stacktrace:
堆栈跟踪:
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
Again, the application works as expected when starting it from eclipse, only executable jar complains about missing csv-files, what are in jar already.
同样,应用程序在从 Eclipse 启动时按预期工作,只有可执行 jar 抱怨缺少 csv 文件,jar 中已经存在的内容。
Any clue would be great.
任何线索都会很棒。
采纳答案by Michael Hegner
Okay, already I found the real problem and the solution.
好的,我已经找到了真正的问题和解决方案。
First, the application use the correct path to the csv files, but there is another issue when using an executable jar what I found under following link. Stackoverflow-Link
首先,应用程序使用正确的 csv 文件路径,但在使用我在以下链接下找到的可执行 jar 时还有另一个问题。Stackoverflow-Link
Before I come to issue with executable jar I used following solution for getting CSV-File (Issue is getFile()):
在我提出可执行 jar 之前,我使用以下解决方案来获取 CSV 文件(问题是 getFile()):
final List<String> resourceLines = FileReadUtils.readLines(specialisationResource.getFile());
for (final String line : resourceLines) {
data.add(getNewTransientSpecialisation(line));
}
But in executeable jar I cant use my resource as file, I need to use it as stream, see provided link above. So I needed to change my code. If you prefer using native java, you can do follows:
但是在可执行的 jar 中,我不能将我的资源用作文件,我需要将它用作流,请参阅上面提供的链接。所以我需要改变我的代码。如果您更喜欢使用本机 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));
}
I prefer using frameworks and use apache commons like follows:
我更喜欢使用框架并使用 apache commons,如下所示:
final List<String> resourceLines = IOUtils.readLines(specialisationResource.getInputStream());
for (final String line : resourceLines) {
data.add(getNewTransientSpecialisation(line));
}
So just remember, don't use File() for getting resource, always use stream do avoid that issue from beginning :-)
所以请记住,不要使用 File() 来获取资源,总是使用流从一开始就避免这个问题:-)
Hope that helps someone.
希望能帮助某人。
回答by Ulises
I encountered this limitation too and created this library to overcome the issue: spring-boot-jar-resources
我也遇到了这个限制并创建了这个库来克服这个问题:spring-boot-jar-resources
It basically allows you to register a custom ResourceLoader with Spring Boot that extracts the classpath resources from the JAR as needed, transparently:
它基本上允许您使用 Spring Boot 注册一个自定义的 ResourceLoader,根据需要从 JAR 中透明地提取类路径资源:
new SpringApplicationBuilder()
.sources(Application.class)
.resourceLoader(new JarResourceLoader())
.run(args);
With that ResourceLoader you can do resource.getFile()
on any classpath resource.
使用该 ResourceLoader,您可以resource.getFile()
在任何类路径资源上执行操作。
回答by Michael Hegner
Now I needed to find xmlFiles into a resource folder from a JAR and facing similar problems described here already. I would like to share my findings and how I got it work, maybe it is helpful.
现在我需要从 JAR 中找到 xmlFiles 到资源文件夹中,并且已经面临这里描述的类似问题。我想分享我的发现以及我是如何让它工作的,也许它会有所帮助。
In a jar I have "db-input" folder under src/main/resources with any number for xml-Files for DB-Input. My application is Spring-Based:
在 jar 中,我在 src/main/resources 下有“db-input”文件夹,其中包含任意数量的 xml-Files for DB-Input。我的应用程序是基于 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);
}
}
}