2026-07-09    2026-07-09    1363 字  3 分钟

一个经典的 Spring Boot + Maven 后端项目目录通常长什么样子呢?

SpringBoot 长什么样

答:像下面这个样子~

demo-project
├── pom.xml
├── README.md
├── .gitignore
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── demo
│   │   │               ├── DemoApplication.java
│   │   │               ├── controller
│   │   │               │   └── UserController.java
│   │   │               ├── service
│   │   │               │   ├── UserService.java
│   │   │               │   └── impl
│   │   │               │       └── UserServiceImpl.java
│   │   │               ├── mapper
│   │   │               │   └── UserMapper.java
│   │   │               ├── entity
│   │   │               │   └── User.java
│   │   │               ├── dto
│   │   │               │   └── UserDTO.java
│   │   │               ├── vo
│   │   │               │   └── UserVO.java
│   │   │               ├── config
│   │   │               │   └── WebConfig.java
│   │   │               ├── common
│   │   │               │   ├── Result.java
│   │   │               │   └── ErrorCode.java
│   │   │               ├── exception
│   │   │               │   ├── BusinessException.java
│   │   │               │   └── GlobalExceptionHandler.java
│   │   │               └── util
│   │   │                   └── DateUtils.java
│   │   └── resources
│   │       ├── application.yml
│   │       ├── application-dev.yml
│   │       ├── application-prod.yml
│   │       ├── mapper
│   │       │   └── UserMapper.xml
│   │       ├── static
│   │       └── templates
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── demo
│                       └── DemoApplicationTests.java

好长啊~ 让我们看看这些核心分层的含义:

controller  接收 HTTP 请求,做参数校验,返回结果
service     业务逻辑
mapper      数据库访问,常见于 MyBatis
entity      数据库表对应的实体类
dto         接收前端请求或服务间传输的数据对象
vo          返回给前端展示的数据对象
config      配置类
common      通用返回体、常量、枚举
exception   自定义异常和全局异常处理
util        工具类
resources   配置文件、Mapper XML、静态资源
test        测试代码

如果是小项目,可以简化成:

controller
service
mapper
entity
common
config

不一定一开始就拆很多包。经典目录的原则是:Controller 不写复杂业务,Service 不直接拼 HTTP,Mapper 只管数据库访问,层与层职责清楚。

有不明白的地方?不要担心,我们后面会讲到的。

这么多文件,这个 Spring Boot 项目启动中、后的流程是什么样子的呢?

先看一下项目入口、配置和主要包结构,然后按实际代码把 Spring Boot 启动链路串起来,而不是只讲通用流程。

项目结构很小,主线集中在 DemoApplication → Controller → Service → Mapper/XML → 数据库

这个项目的启动流程可以按两条线理解:应用启动流程请求处理流程

应用启动流程

入口在 DemoApplication.java ,如下:

1
2
3
4
5
6
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

启动时大致发生这些事:

1. JVM 执行 main 方法。

2. SpringApplication.run(...) 创建并启动 Spring 容器。

3. @SpringBootApplication 生效,它包含三类核心能力:

  • 标记这是 Spring Boot 应用
  • 开启自动配置,比如 Web、JSON、数据源、MyBatis 等
  • com.example.demo 包开始扫描Bean

4. Spring 扫描到这些组件

  • UserController
  • UserServiceImpl
  • UserMapper
  • GlobalExceptionHandler
  • WebConfig

5. 读取 application.yml ,默认激活 dev 环境。

6. 继续读取 application-dev.yml,配置 H2 内存数据库。

7. 因为 spring.sql.init.mlde: always,启动时会执行

  • schema.sql 创建 users
  • data.sql 插入 tomalice 两条演示数据

8. MyBatis 根据配置加载 UserMapper.xml

9. 内嵌 Tomcat 启动,监听 8080 端口。

如此,启动后,我们叫可以访问:

GET  http://localhost:8080/api/users
GET  http://localhost:8080/api/users/1
POST http://localhost:8080/api/users

那么,在请求访问的时候,又经过了哪些流程呢?

请求处理流程

一次请求的大致流向:

前端请求
  -> Controller
  -> Service
  -> Mapper
  -> Database
  -> Mapper
  -> Service
  -> Controller
  -> JSON 响应

下面,我们以查询用户列表为例,具体说明一下整个流程,如下:

浏览器/前端:
  → UserController
  → UserService
  → UserServiceImpl
  → UserMapper
  → UserMapper.xml
  → H2 数据库
  → 返回 Entity
  → 转成 VO
  → Result.success(...)
  → JSON 回应

我们来看看对应的代码链路:

1. 请求进入 UserController.java

1
2
3
4
@GetMapping
public Result<List<UserVO>> listUsers() {
    return Result.success(userService.listUsers());
}

2. Controller 调用 UserService.java 接口

3. 实际执行的是 UserServiceImpl.java ,因为它有:

1
2
@Service
public class UserServiceImpl implements UserService

4. Service 调用 UserMapper.java .

5. MyBatis 找到 UserMapper.xml 里的 SQL :

1
2
3
4
5
<select id="findAll" resultMap="UserResultMap">
    ELECT id, username, email, age, created_at
    FROM users
    ORDER BY id ASC
</select>

6. 查询结果先映射成 User ,再由 Service 正成 UserVO ,最后 Controller 包装成 Result.success(...) 返回。

请求过程中出现了问题怎么办呢?

异常流程处理

如果发生错误,不是在 Controller 里手写 try-catch ,而是进入 GlobalExceptionHandler.java

比如访问不存在的用户 GET /api/users/999 ,流程是:

UserController.getUser(...)
  → UserServiceImpl.getUser(...)
  → userMapper.findById(id)
  → 查不到,抛 BusinessException
  → GlobalExceptionHandler 捕获
  → Result.fail(...)
  → JSON 错误响应