Spring Boot Jar启动时owners.json文件FileNotFoundException问题排查
解决Spring Boot Jar启动时Classpath资源读取失败的问题
这个问题我之前做项目时也踩过坑,核心原因其实很好理解:当你用mvn spring-boot:run或者IDE直接运行时,项目资源是存放在本地文件系统里的,所以能用File类直接访问路径;但打包成Jar后,这些JSON文件是Jar包内部的压缩条目,不再是独立的物理文件,File类自然找不到它们。
解决方案:用流的方式读取Classpath资源
Spring提供了非常成熟的资源处理机制,不需要自己硬编码文件路径,直接通过流读取即可。下面给你两种可行的修改方案:
方案1:注入ResourceLoader读取资源
修改你的DataInserter组件,通过构造函数注入Spring的ResourceLoader来获取资源流:
@Component public class DataInserter implements ApplicationListener<ContextRefreshedEvent> { private final ResourceLoader resourceLoader; private final OwnerRepository ownerRepository; private final CarRepository carRepository; // 构造函数注入依赖(Spring 4.3+支持无@Autowired的构造注入) public DataInserter(ResourceLoader resourceLoader, OwnerRepository ownerRepository, CarRepository carRepository) { this.resourceLoader = resourceLoader; this.ownerRepository = ownerRepository; this.carRepository = carRepository; } @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 读取并初始化owners数据 try (InputStream ownerStream = resourceLoader.getResource("classpath:data/owners.json").getInputStream()) { ObjectMapper mapper = new ObjectMapper(); List<Owner> owners = mapper.readValue(ownerStream, new TypeReference<List<Owner>>() {}); ownerRepository.saveAll(owners); } catch (IOException e) { throw new RuntimeException("加载车主数据失败", e); } // 读取并初始化cars数据 try (InputStream carStream = resourceLoader.getResource("classpath:data/cars.json").getInputStream()) { ObjectMapper mapper = new ObjectMapper(); List<Car> cars = mapper.readValue(carStream, new TypeReference<List<Car>>() {}); carRepository.saveAll(cars); } catch (IOException e) { throw new RuntimeException("加载车辆数据失败", e); } } }
方案2:用@Value直接注入Resource对象
如果觉得注入ResourceLoader有点繁琐,可以直接用@Value注解注入对应的资源对象:
@Component public class DataInserter implements ApplicationListener<ContextRefreshedEvent> { @Value("classpath:data/owners.json") private Resource ownerResource; @Value("classpath:data/cars.json") private Resource carResource; private final OwnerRepository ownerRepository; private final CarRepository carRepository; public DataInserter(OwnerRepository ownerRepository, CarRepository carRepository) { this.ownerRepository = ownerRepository; this.carRepository = carRepository; } @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 处理车主数据 try (InputStream stream = ownerResource.getInputStream()) { ObjectMapper mapper = new ObjectMapper(); List<Owner> owners = mapper.readValue(stream, new TypeReference<List<Owner>>() {}); ownerRepository.saveAll(owners); } catch (IOException e) { throw new RuntimeException("加载车主数据失败", e); } // 处理车辆数据(逻辑和上面一致) try (InputStream stream = carResource.getInputStream()) { ObjectMapper mapper = new ObjectMapper(); List<Car> cars = mapper.readValue(stream, new TypeReference<List<Car>>() {}); carRepository.saveAll(cars); } catch (IOException e) { throw new RuntimeException("加载车辆数据失败", e); } } }
关键注意事项
- 必须用**流(InputStream)**读取Jar内的资源,绝对不要尝试用
File或Path类访问,因为Jar里的资源不是文件系统中的实体。 - 使用try-with-resources语法(
try (InputStream ...))确保流自动关闭,避免资源泄漏。 - 打包后验证:可以用
jar tf your-app.jar命令查看,确认data/owners.json和data/cars.json在Jar的BOOT-INF/classes/data/目录下(Spring Boot打包后资源的默认位置),如果不在,检查src/main/resources/data/目录是否正确,确保Maven打包时会包含这些资源。
修改后不管是用mvn spring-boot:run还是java -jar your-app.jar启动,都能正常读取资源初始化数据库了。
内容的提问来源于stack exchange,提问作者PacificNW_Lover
相关产品推荐
相关产品推荐

