以jarforms运行时找不到类path资源

在Spring Boot 1.1.5和1.1.6中都有这个问题 – 我使用@Value注解加载类path资源,在STS(3.6.0,Windows)中运行应用程序时工作得很好。 但是,当我运行一个mvn包,然后尝试运行该jar,我得到FileNotFoundexception。

资源message.txt位于src / main / resources中。 我已经检查了这个jar,并确认它包含顶层的文件“message.txt”(与application.properties同级别)。

这是应用程序:

@Configuration @ComponentScan @EnableAutoConfiguration public class Application implements CommandLineRunner { private static final Logger logger = Logger.getLogger(Application.class); @Value("${message.file}") private Resource messageResource; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override public void run(String... arg0) throws Exception { // both of these work when running as Spring boot app from STS, but // fail after mvn package, and then running as java -jar testResource(new ClassPathResource("message.txt")); testResource(this.messageResource); } private void testResource(Resource resource) { try { resource.getFile(); logger.debug("Found the resource " + resource.getFilename()); } catch (IOException ex) { logger.error(ex.toString()); } } } 

例外:

 c:\Users\glyoder\Documents\workspace-sts-3.5.1.RELEASE\classpath-resource-proble m\target>java -jar demo-0.0.1-SNAPSHOT.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.1.5.RELEASE) 2014-09-16 08:46:34.635 INFO 5976 --- [ main] demo.Application : Starting Application on 8W59XV1 with PID 5976 (C:\Users\glyo der\Documents\workspace-sts-3.5.1.RELEASE\classpath-resource-problem\target\demo -0.0.1-SNAPSHOT.jar started by glyoder in c:\Users\glyoder\Documents\workspace-s ts-3.5.1.RELEASE\classpath-resource-problem\target) 2014-09-16 08:46:34.640 DEBUG 5976 --- [ main] demo.Application : Running with Spring Boot v1.1.5.RELEASE, Spring v4.0.6.RELEA SE 2014-09-16 08:46:34.681 INFO 5976 --- [ main] scaAnnotationConfigA pplicationContext : Refreshing org.springframework.context.annotation.Annotation ConfigApplicationContext@1c77b086: startup date [Tue Sep 16 08:46:34 EDT 2014]; root of context hierarchy 2014-09-16 08:46:35.196 INFO 5976 --- [ main] osjeaAnnotationMBe anExporter : Registering beans for JMX exposure on startup 2014-09-16 08:46:35.210 ERROR 5976 --- [ main] demo.Application : java.io.FileNotFoundException: class path resource [message. txt] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/C:/Users/glyoder/Documents/workspace-sts-3.5.1.RELEASE/cl asspath-resource-problem/target/demo-0.0.1-SNAPSHOT.jar!/message.txt 2014-09-16 08:46:35.211 ERROR 5976 --- [ main] demo.Application : java.io.FileNotFoundException: class path resource [message. txt] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/C:/Users/glyoder/Documents/workspace-sts-3.5.1.RELEASE/cl asspath-resource-problem/target/demo-0.0.1-SNAPSHOT.jar!/message.txt 2014-09-16 08:46:35.215 INFO 5976 --- [ main] demo.Application : Started Application in 0.965 seconds (JVM running for 1.435) 2014-09-16 08:46:35.217 INFO 5976 --- [ Thread-2] scaAnnotationConfigA pplicationContext : Closing org.springframework.context.annotation.AnnotationCon figApplicationContext@1c77b086: startup date [Tue Sep 16 08:46:34 EDT 2014]; roo t of context hierarchy 2014-09-16 08:46:35.218 INFO 5976 --- [ Thread-2] osjeaAnnotationMBe anExporter : Unregistering JMX-exposed beans on shutdown 

resource.getFile()期望资源本身在文件系统上可用,即它不能嵌套在jar文件中。 这就是为什么它在STS中运行应用程序时会起作用,但是在构build应用程序并从可执行程序jar中运行应用程序后,它将不起作用。 而不是使用getFile()来访问资源的内容,我build议使用getInputStream()来代替。 这将允许您阅读资源的内容,无论它位于何处。

如果你想使用jks文件。 我想到了代码

 ClassPathResource classPathResource = new ClassPathResource("static/something.txt"); InputStream inputStream = classPathResource.getInputStream(); File somethingFile = File.createTempFile("test", ".txt"); try { FileUtils.copyInputStreamToFile(inputStream, somethingFile); } finally { IOUtils.closeQuietly(inputStream); } 

如果您使用的是Spring框架,那么使用Spring框架的FileCopyUtils ClassPathResource读入String非常简单:

 String data = ""; ClassPathResource cpr = new ClassPathResource("static/file.txt"); try { byte[] bdata = FileCopyUtils.copyToByteArray(cpr.getInputStream()); data = new String(bdata, StandardCharsets.UTF_8); } catch (IOException e) { LOG.warn("IOException", e); } 

当spring启动项目以jar的forms运行,需要在classpath中读取一些文件时,我通过下面的代码来实现它

 Resource resource = new ClassPathResource("data.sql"); BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream())); reader.lines().forEach(System.out::println); 

我也遇到了这个限制,并且创build了这个库来解决这个问题: spring-boot-jar-resources它基本上允许你用Spring Boot注册一个自定义的ResourceLoader,它透明地根据需要从JAR中提取类path资源。

泽西岛需要被打开jar子。

 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <requiresUnpack> <dependency> <groupId>com.myapp</groupId> <artifactId>rest-api</artifactId> </dependency> </requiresUnpack> </configuration> </plugin> </plugins> </build> 

我已经创build了一个java 8类的ClassPathResourceReader类,以便从classpath轻松读取文件

 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.stream.Collectors; import org.springframework.core.io.ClassPathResource; public final class ClassPathResourceReader { private final String path; private String content; public ClassPathResourceReader(String path) { this.path = path; } public String getContent() { if (content == null) { try { ClassPathResource resource = new ClassPathResource(path); BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream())); content = reader.lines().collect(Collectors.joining("\n")); reader.close(); } catch (IOException ex) { throw new RuntimeException(ex); } } return content; } } 

利用:

 String content = new ClassPathResourceReader("data.sql").getContent();