Spring Boot Jar运行时File加载类路径properties报FileNotFoundException
问题解答
路径中的感叹号不是报错的根本原因。java.io.File 只能访问文件系统中存在的独立文件,无法直接读取Jar压缩包内部的资源条目,这才是问题核心。
- IDE中运行正常的原因:IDE运行时会把编译后的class、资源文件直接输出到
target/classes目录,所有资源都是磁盘上的独立文件,File可以正常寻址访问。 - Jar运行报错的原因:打包后所有类路径资源都被压缩进Jar这个单一归档文件内,不存在独立的
components.properties磁盘文件,自然无法通过File找到对应资源。
路径中的!是Jar URL协议的标准分隔符,作用是分割Jar包本身的磁盘路径和包内资源的路径,不属于非法字符。你代码中调用resource.getFile()拿到的只是URL的路径片段,不是合法的文件系统路径,用它构造File对象本身就是错误用法,仅在资源是磁盘独立文件时该方式才有效。
另外你代码中对该配置文件加了变更监听逻辑,Jar包内部的资源是只读的,运行时不会发生变更,该监听逻辑在Jar运行场景下本身就不成立。
修复方案
- 读取类路径内置资源时,不要通过
File访问,直接获取输入流读取即可。将loadConfigurations方法的入参从File改为InputStream,直接调用类加载器的getResourceAsStream()方法获取流,兼容IDE运行、Jar运行等所有场景:
// 替换原有获取File的逻辑 try (InputStream is = this.getClass().getResourceAsStream("/" + ComponentConfigConstants.COMPONENT_CONFIGURATION_FILE)) { props.load(is); }
- Spring Boot项目优先使用内置的
ClassPathResource读取类路径资源,自动适配各种部署场景:
ClassPathResource configResource = new ClassPathResource(ComponentConfigConstants.COMPONENT_CONFIGURATION_FILE); try (InputStream is = configResource.getInputStream()) { props.load(is); }
- 如果需要实现配置热更新,不要将配置文件打包在Jar内部,应该放在Jar外部的固定目录(比如Jar同层级的
config文件夹),运行时从外部文件路径读取,既可以通过File正常访问,也能实现变更监听。
问题背景
- 异常场景:Spring Boot项目打包为可执行Jar后运行时触发,IntelliJ IDEA内直接运行无异常
- 异常类型:
java.io.FileNotFoundException - 异常时解析到的资源路径:
file:\C:\workspaces\localdb-service\target\localdb-service-0.0.1-SNAPSHOT.jar!\BOOT-INF\classes!\components.properties
- 核心疑问:路径中的感叹号是否为报错原因,该问题的触发逻辑是什么
相关业务代码
public static void loadConfigurations(File propsFile) throws ComponentConfigIntializationException{ FileInputStream is = null; try { if(props ==null){ props = new Properties(getDefaultConfigurationValues()); } is = new FileInputStream(propsFile); props.load(is); populateProperties(); configurationInitialized = true; }catch (Exception e) { LGR.info("Exception# error occurred in loadConfigurations"); LGR.info(e); throw new ComponentConfigIntializationException(e.getCause()); } } public void init() throws ServletException { try{ LGR.info(LGR.isInfoEnabled() ? "--- ComponentConfigurationLoader.init() , Loading component Configurations" : null); URL resource = this.getClass().getResource("/"+ ComponentConfigConstants.COMPONENT_CONFIGURATION_FILE); File componentConfigFile = new File(resource.getFile()); if(componentConfigFile.exists()){ LGR.info(LGR.isInfoEnabled() ? "--- Component.properties exits" : null); }else{ LGR.info(LGR.isInfoEnabled() ? "--- Component.properties does not exits" + componentConfigFile.getPath(): null); } ComponentConfigurationProvider.loadConfigurations(componentConfigFile); ComponentConfigurationProvider.setRealPath(getServletContext().getRealPath("/")); LGR.info(LGR.isInfoEnabled() ? "Configurations Loaded, Starting monitor to observee refresh changings" : null); ConfigurationRefresher.startConfigurationMonitoring(componentConfigFile, ComponentConfigurationProvider.getConfigurationRefresherTime()); initializeSecurityConfigruation(ComponentConfigConstants.SECURITY_FRAME_POLICIES_CONFIGURATION_FILE,true); initializeSecurityConfigruation(ComponentConfigConstants.ALLOW_CROSS_DOMAIN_CONFIGURATION_FILE,false); initializeBuildConfiguration(ComponentConfigConstants.BUILD_FILE); }catch(ComponentConfigIntializationException e){ LGR.error("#COMPONENT_CONFIG_LOAD_ERROR# Error While Loading Commmon Configurations", e); } }
报错日志
2022-06-21 14:22:43.361 INFO 17392 --- [ main] c.i.c.f.s.ComponentConfigurationLoader : --- ComponentConfigurationLoader.init() , Loading component Configurations 2022-06-21 14:22:43.361 INFO 17392 --- [ main] c.i.c.f.s.ComponentConfigurationLoader : --- Component.properties does not exitsfile:\C:\workspaces\localdb-service\target\localdb-service-0.0.1-SNAPSHOT.jar!\BOOT-INF\classes!\components.properties 2022-06-21 14:22:43.361 INFO 17392 --- [main] c.i.c.f.c.ComponentConfigurationProvider : Exception# error occurred in loadConfigurations 2022-06-21 14:22:43.361 INFO 17392 --- [ main] c.i.c.f.c.ComponentConfigurationProvider : java.io.FileNotFoundException: file:\C:\workspaces\localdb-service\target\localdb-service-0.0.1-SNAPSHOT.jar!\BOOT-INF\classes!\components.properties (The filename, directory name, or volume label syntax is incorrect) 2022-06-21 14:22:43.376 ERROR 17392 --- [main] c.i.c.f.s.ComponentConfigurationLoader : #COMPONENT_CONFIG_LOAD_ERROR# Error While Loading Commmon Configurations com.i2c.components.framework.exceptions.ComponentConfigIntializationException: null at com.i2c.components.framework.config.ComponentConfigurationProvider.loadConfigurations(ComponentConfigurationProvider.java:187) ~[Components3-Core-1.0.jar!/:na] at com.i2c.components.framework.startup.ComponentConfigurationLoader.init(ComponentConfigurationLoader.java:53) ~[Components3-Core-1.0.jar!/:na] at javax.servlet.GenericServlet.init(GenericServlet.java:203) [javax.servlet-api-4.0.1.jar!/:4.0.1]
内容的提问来源于stack exchange,提问作者Hammad Ahmad
相关产品推荐
相关产品推荐

