1. 概述

Spring Boot 项目在组织类文件结构时,灵活性很高,你可以按自己喜欢的方式安排。但为了可维护性和团队协作,有一些最佳实践值得遵循

虽然不强制,但遵循这些约定能避免踩坑、提升代码可读性,尤其在项目变大后优势更明显。本文总结了主流推荐的包结构设计方式,适合中高级开发者参考。

2. 不要使用默认包 ❌

Spring Boot 的核心机制依赖包路径进行类扫描,比如:

  • @ComponentScan
  • @EntityScan
  • @ConfigurationPropertiesScan
  • @SpringBootApplication(它自带 @ComponentScan

这些注解默认会扫描主类所在包及其子包。如果你的启动类放在默认包(即没有 package 声明),会导致:

✅ 扫描范围不可控
✅ 部分组件可能无法被加载
✅ JPA 实体扫描失败

⚠️ 所以:务必显式声明包名,哪怕是最简单的项目。

// ❌ 错误示范:没有 package 声明
// public class Application { ... }

// ✅ 正确做法
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

3. 主类(Main Class)的位置 ✅

@SpringBootApplication 注解会自动启用组件扫描,扫描范围是主类所在的包及其所有子包

因此,最佳实践是:

将主类放在项目的根包(base package)下

这样能确保所有模块都被自动扫描到,无需额外配置。

可配置但没必要

你当然可以通过 scanBasePackages 手动指定扫描路径:

@SpringBootApplication(scanBasePackages = "com.example")
@EnableJpaRepositories("com.example.repository")
@EntityScan("com.example.domain")
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

但这增加了维护成本,也容易出错。除非有特殊需求(比如模块拆分、多数据源),否则简单粗暴地把主类放根包就行

JPA 项目额外注意

如果你用 JPA/Hibernate,建议显式加上:

  • @EnableJpaRepositories:指定 Repository 接口位置
  • @EntityScan:指定实体类位置

虽然在大多数情况下它们能自动推断,但在复杂项目或跨模块时容易漏扫,显式声明更稳妥。

4. 包结构设计:按功能划分(Package-by-Feature)✅

包结构设计本身不依赖 Spring Boot,而是由业务需求决定。但有一个被广泛采用的模式:按功能划分包(package-by-feature)

相比“按层划分”(如 controller, service, dao),这种方式更利于模块化,也更容易实现高内聚、低耦合。

什么是按功能划分?

每个包代表一个业务功能或领域模型,内部包含该功能所需的所有类(Controller、Service、Entity、Repository 等)。

举个经典例子:Spring PetClinic

它的包结构如下:

org.springframework.samples.petclinic
├── model
├── owner
├── system
├── vet
└── visit

每个子包对应一个业务模块:

  • owner:宠物主人管理
  • vet:兽医信息
  • visit:就诊记录
  • model:共享实体基类
  • system:系统配置(如错误页面、健康检查)

优势对比

方式 优点 缺点
按层划分(Layer-based) 初学者易懂,结构清晰 跨模块跳转频繁,修改一个功能要打开多个包
按功能划分(Feature-based) 高内聚,易维护,适合团队协作 需要一定设计能力

✅ 推荐:中大型项目一律采用按功能划分

示例结构

以一个电商项目为例:

com.example.ecommerce
├── product          // 商品管理
│   ├── Product.java
│   ├── ProductRepository.java
│   ├── ProductController.java
│   └── ProductService.java
├── order            // 订单模块
│   ├── Order.java
│   ├── OrderRepository.java
│   ├── OrderController.java
│   └── OrderService.java
├── payment          // 支付模块
├── user             // 用户中心
└── EcommerceApplication.java

这样,当你修改“订单”功能时,所有相关代码都在 order 包里,不用满项目找文件。

5. 总结

  • ❌ 不要用默认包,必须显式声明 package
  • ✅ 主类放在根包,让 @ComponentScan 自动覆盖所有子模块
  • ✅ 优先采用 按功能划分包结构(package-by-feature),提升模块化程度
  • ✅ 复杂项目中,显式声明 @EntityScan@EnableJpaRepositories 更安全
  • ⚠️ 包结构一旦定型,后期重构成本高,初期设计要慎重

遵循这些原则,你的 Spring Boot 项目不仅结构清晰,还能轻松应对后续扩展和团队协作。


原始标题:Recommended Package Structure of a Spring Boot Project