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

SpringBoot应用类中初始化Service与Repository类的解决方案

解决SpringBoot启动后自动装配失败的问题

这个问题我之前踩过坑!核心原因是:你的主类的main方法是Java程序的入口,虽然SpringApplication.run()会启动Spring上下文,但主类本身默认不是Spring管理的Bean——所以你在主类里加的@Autowired注解根本不会被Spring处理,自然拿不到Service和Repository的实例。

最优雅的解决方案是用SpringBoot提供的CommandLineRunner或ApplicationRunner接口,它们专门用来在Spring上下文完全初始化后执行自定义逻辑,而且这些类是Spring Bean,能正常享受依赖注入的特性。

具体实现步骤

1. 简化主类

主类只需要保留启动逻辑即可,不用写业务代码:

@SpringBootApplication
public class StdinH2DemoApp {
    public static void main(String[] args) {
        SpringApplication.run(StdinH2DemoApp.class, args);
    }
}

2. 创建CommandLineRunner实现类

这个类会在应用启动完成后自动执行,我们在这里处理stdin输入、数据存储和stdout输出:

@Component
public class DataProcessingRunner implements CommandLineRunner {

    // 用构造函数注入(Spring官方推荐,比@Autowired更安全,避免NPE)
    private final DataService dataService;

    public DataProcessingRunner(DataService dataService) {
        this.dataService = dataService;
    }

    @Override
    public void run(String... args) throws Exception {
        // 从stdin读取输入
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入要存储的数据:");
        String inputContent = scanner.nextLine();
        scanner.close();

        // 调用Service存储到H2数据库
        DataEntity savedData = dataService.saveData(inputContent);

        // 在stdout展示结果
        System.out.println("\n数据已成功存储到H2数据库:");
        System.out.printf("ID: %d\n内容: %s\n创建时间: %s\n", 
                          savedData.getId(), 
                          savedData.getContent(), 
                          savedData.getCreatedAt());
    }
}

3. 编写Service和Repository

这部分就是常规的Spring数据层和业务层代码,完全正常使用依赖注入:

@Service
public class DataService {
    private final DataRepository dataRepository;

    public DataService(DataRepository dataRepository) {
        this.dataRepository = dataRepository;
    }

    public DataEntity saveData(String content) {
        DataEntity entity = new DataEntity();
        entity.setContent(content);
        entity.setCreatedAt(LocalDateTime.now());
        return dataRepository.save(entity);
    }
}
@Repository
public interface DataRepository extends JpaRepository<DataEntity, Long> {
    // 继承JpaRepository就拥有了基本的CRUD方法,不用自己写SQL
}

4. 定义H2对应的实体类

@Entity
@Table(name = "data_records")
public class DataEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String content;
    
    private LocalDateTime createdAt;

    // 这里可以用Lombok的@Data注解自动生成getter/setter,也可以手动写
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

为什么这种方式更好?

  • 符合Spring设计原则:CommandLineRunner是SpringBoot官方提供的扩展点,确保在所有Bean都初始化完成后执行逻辑,不会出现依赖未注入的问题。
  • 可维护性高:业务逻辑和启动逻辑分离,代码结构更清晰,单元测试时可以轻松Mock依赖。
  • 灵活扩展:如果需要处理命令行参数,run方法的String... args可以直接接收;如果需要更复杂的参数解析(比如带选项的参数),可以换成ApplicationRunner接口,它提供了ApplicationArguments来处理结构化参数。

为什么主类里的方式不行?

主类的main方法是静态入口,Spring容器启动后,不会自动将依赖注入到主类的实例中(除非你给主类加@Component,但这不符合代码规范)。所以你在SpringApplication.run()之后写的逻辑里,@Autowired的字段都是null,自然会报错。

内容的提问来源于stack exchange,提问作者BrownTownCoder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:14:37