You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 22:07:26