Spring Boot 项目搭建框架和流程详解

以员工管理为例

3688字

Spring Boot 项目开发流程详解 (以员工管理为例)

这个案例开发流程是一个典型的基于 Spring Boot 的分层架构实现。下面我将详细阐述每个步骤及其核心思想,希望能让您对这个流程有一个清晰的理解。


核心目标: 实现模块化、高内聚、低耦合,使得代码易于理解、维护、测试和扩展。

流程总览:
请求 (Request) → Controller 层 → Service 层 (接口+实现) → Mapper 层 (DAO) → 数据库 (Database) → Mapper 层 → Service 层 → Controller 层 → 响应 (Response)

1. POJO 层 (Plain Old Java Object) / Entity 层

目的

定义数据模型,用于封装业务数据。这些类通常与数据库表结构相对应。

具体实现

创建 Emp.java (员工信息实体类) 和 Empr.java (员工工作经历实体类)。

Emp.java 示例

java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.example.pojo;

import java.util.List; // 如果需要在Emp中直接持有Empr列表
// import lombok.Data; // 使用Lombok简化代码,可选

// @Data // Lombok注解,自动生成getter/setter/toString/equals/hashCode
public class Emp {
    private Integer empId;
    private String empName;
    private String department;
    private Double salary;
    // private List<Empr> workExperiences; // 可选,用于一对多关系

    // Standard getters and setters, constructor, toString, etc.
    // 如果不用Lombok,需要手动添加
    public Integer getEmpId() { return empId; }
    public void setEmpId(Integer empId) { this.empId = empId; }
    // ... 其他属性的getter/setter
}

Empr.java 示例

java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package com.example.pojo;

import java.util.Date;
// import lombok.Data;

// @Data
public class Empr { // Employee Experience Record
    private Integer emprId;
    private Integer empId; // 外键,关联到Emp表的empId
    private String companyName;
    private String position;
    private Date startDate;
    private Date endDate;
    private String description;

    // Standard getters and setters, constructor, toString, etc.
}

关键点

  • 属性:与数据库表字段一一对应,或根据业务需求定义。
  • 注解(可选,但常用)
    • Lombok@Data@Getter@Setter@ToString@EqualsAndHashCode@NoArgsConstructor@AllArgsConstructor 等注解可以极大简化代码,避免手动编写样板代码。
    • JPA/MyBatis-Plus: 如果使用 JPA 或 MyBatis-Plus 等 ORM 框架,还会用到 @Entity@Table@Id@Column@TableId@TableName 等注解来映射数据库表和字段。
  • 封装性:属性通常声明为 private,通过 public 的 getter 和 setter 方法访问。

2. Mapper 层 (DAO - Data Access Object)

目的

定义数据访问接口,负责与数据库进行直接交互,执行 SQL 语句(增删改查)。

具体实现

创建 EmpMapper.java 和 EmpEmprMapper.java 接口,并在接口上添加 @Mapper 注解。

@Mapper 注解作用

  • 这是 MyBatis 的注解 (或者是 MyBatis-Plus 提供的,效果类似)。
  • Spring Boot 在启动时会扫描带有 @Mapper 注解的接口,并为它们创建代理实现类,然后将这些代理实例注册到 Spring IOC 容器中。这样,在 Service 层就可以通过 @Autowired 自动注入这些 Mapper 接口的实例。

EmpMapper.java 示例

java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.example.mapper;

import com.example.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; // 多个参数时建议使用
import java.util.List;

@Mapper
public interface EmpMapper {
    Emp findById(@Param("empId") Integer empId);
    List<Emp> findAll();
    int insertEmp(Emp emp);
    int updateEmp(Emp emp);
    int deleteEmp(@Param("empId") Integer empId);
}

EmpEmprMapper.java 示例

java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.example.mapper;

import com.example.pojo.Empr;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;

@Mapper
public interface EmpEmprMapper {
    List<Empr> findByEmpId(@Param("empId") Integer empId);
    int insertEmpr(Empr empr);
    // ... 其他CRUD方法
}

关键点

  • 接口定义:只定义方法签名,不包含具体实现。
  • SQL 实现
    • XML 文件:通常与 Mapper 接口同路径或在 resources 目录下创建对应的 XML 文件 (如 EmpMapper.xml) 来编写 SQL 语句。
    • 注解方式:对于简单 SQL,也可以直接使用 @Select@Insert@Update@Delete 等注解直接在接口方法上编写 SQL。
  • 方法命名:通常遵循一定的规范,如 findByXXXinsertXXXupdateXXXdeleteXXX
  • 参数与返回:方法的参数对应 SQL 中的条件,返回值对应查询结果。

3. Service 层

目的

处理核心业务逻辑,对一个或多个 Mapper 操作进行编排和封装,形成一个完整的业务功能。它起到承上启下的作用,隔离 Controller 层和 Mapper 层。

分为两部分:接口和实现类

3.1 Service 接口 (EmpService.java)

目的:定义业务契约,声明业务方法。这样做的好处是面向接口编程,提高代码的灵活性和可测试性 (方便 Mock)。

具体实现

java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.example.service;

import com.example.pojo.Emp;
// import com.example.dto.EmpDetailDTO; // 可能需要DTO
import java.util.List;

public interface EmpService {
    Emp getEmployeeById(Integer empId);
    // EmpDetailDTO getEmployeeWithExperience(Integer empId); // 示例:返回包含经历的员工信息
    List<Emp> getAllEmployees();
    void addNewEmployee(Emp emp /*, List<Empr> experiences*/); // 可能同时添加员工和经历
    void updateEmployeeInfo(Emp emp);
    void removeEmployee(Integer empId);
}

3.2 Service 实现类 (EmpServiceImpl.java)

目的:具体实现 Service 接口中定义的业务逻辑。

具体实现

  • 创建 impl 包 (约定俗成),在包内创建 EmpServiceImpl.java 类。
  • 实现 EmpService 接口 (implements EmpService)。
  • 添加 @Service 注解。

@Service 注解作用

  • 这是 Spring 框架的 stereotype (构造型) 注解之一,用于标识这是一个业务逻辑层组件。
  • Spring Boot 启动时会扫描带有 @Service 注解的类,并创建其实例注册到 Spring IOC 容器中,使其成为一个 Bean。这样,在 Controller 层就可以通过 @Autowired 自动注入 EmpService 接口的实例 (Spring 会自动找到其实现类 EmpServiceImpl 的实例)。

EmpServiceImpl.java 示例

java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.example.service.impl;

import com.example.mapper.EmpEmprMapper;
import com.example.mapper.EmpMapper;
import com.example.pojo.Emp;
import com.example.pojo.Empr;
import com.example.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; // 引入事务注解

import java.util.List;

@Service // 声明为Spring IOC容器的Bean
public class EmpServiceImpl implements EmpService {

    @Autowired // 自动注入EmpMapper实例
    private EmpMapper empMapper;

    @Autowired // 自动注入EmpEmprMapper实例
    private EmpEmprMapper empEmprMapper;

    @Override
    public Emp getEmployeeById(Integer empId) {
        // 简单示例:直接调用mapper
        return empMapper.findById(empId);
    }

    /*
    // 复杂业务示例:获取员工及其工作经历
    @Override
    public EmpDetailDTO getEmployeeWithExperience(Integer empId) {
        Emp emp = empMapper.findById(empId);
        if (emp == null) {
            return null;
        }
        List<Empr> experiences = empEmprMapper.findByEmpId(empId);
        // 假设EmpDetailDTO是专门用于封装员工和其经历的类
        EmpDetailDTO dto = new EmpDetailDTO();
        dto.setEmpId(emp.getEmpId());
        dto.setEmpName(emp.getEmpName());
        // ... 其他emp属性
        dto.setWorkExperiences(experiences);
        return dto;
    }
    */

    @Override
    public List<Emp> getAllEmployees() {
        return empMapper.findAll();
    }

    @Override
    @Transactional // 声明式事务:保证方法内多个数据库操作的原子性
    public void addNewEmployee(Emp emp /*, List<Empr> experiences*/) {
        empMapper.insertEmp(emp); // 插入员工信息,emp对象通常会配置返回自增主键
        // if (experiences != null && !experiences.isEmpty()) {
        //     for (Empr expr : experiences) {
        //         expr.setEmpId(emp.getEmpId()); // 设置外键
        //         empEmprMapper.insertEmpr(expr);
        //     }
        // }
        // 如果在插入经历时发生错误,整个操作会回滚
    }

    @Override
    @Transactional
    public void updateEmployeeInfo(Emp emp) {
        empMapper.updateEmp(emp);
        // 可能还需要更新相关的工作经历等
    }

    @Override
    @Transactional
    public void removeEmployee(Integer empId) {
        // 实际业务中可能需要先删除关联的工作经历,或有其他逻辑检查
        // empEmprMapper.deleteByEmpId(empId); // 假设有此方法
        empMapper.deleteEmp(empId);
    }
}

关键点

  • 依赖注入 (@Autowired):通过 @Autowired 注解将 Mapper 层的实例注入到 Service 实现类中。
  • 业务逻辑封装:包含条件判断、数据转换、多个 DAO 操作的组合等。
  • 事务管理 (@Transactional):对于涉及多个写操作(增、删、改)的业务方法,通常会使用 @Transactional 注解来保证操作的原子性。如果方法中任意一个数据库操作失败,整个事务会回滚,保证数据一致性。

4. Controller 层

目的

接收前端 HTTP 请求,调用 Service 层处理业务逻辑,并将处理结果返回给前端。它是应用的入口点。

具体实现

创建 EmpController.java 类,并添加 @RestController 注解。

@RestController 注解作用

  • 这是一个组合注解,相当于 @Controller + @ResponseBody
  • @Controller:将类标识为一个控制器组件,Spring IOC 容器会管理它。
  • @ResponseBody:表示该控制器中所有方法的返回值都会直接作为 HTTP 响应体的内容(通常是 JSON 或 XML 格式),而不是视图名称。

@Slf4j (可选)

  • 这是 Lombok 提供的注解,用于快速集成 SLF4J 日志框架。它会在编译时自动为类添加一个 log 字段 ( private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EmpController.class); )。
  • 作用:方便记录日志,用于调试、追踪问题和监控应用运行状态。

EmpController.java 示例

java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.example.controller;

import com.example.pojo.Emp;
// import com.example.dto.EmpDetailDTO;
import com.example.service.EmpService;
import lombok.extern.slf4j.Slf4j; // 如果使用@Slf4j
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController // 声明为RESTful风格的控制器,方法返回JSON/XML
@RequestMapping("/api/employees") // 类级别的请求路径映射,所有方法都在此基础路径下
@Slf4j // 可选,用于日志记录
public class EmpController {

    @Autowired // 自动注入EmpService实例
    private EmpService empService;

    // GET /api/employees/{id} - 获取指定ID的员工信息
    @GetMapping("/{id}")
    public ResponseEntity<?> getEmployeeById(@PathVariable Integer id) {
        log.info("Request to get employee with ID: {}", id);
        // EmpDetailDTO empDetail = empService.getEmployeeWithExperience(id);
        Emp employee = empService.getEmployeeById(id);
        if (employee != null) {
            log.info("Found employee: {}", employee.getEmpName());
            return ResponseEntity.ok(employee); // 返回200 OK 和员工数据
        } else {
            log.warn("Employee with ID: {} not found.", id);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Employee not found"); // 返回404 Not Found
        }
    }

    // GET /api/employees - 获取所有员工信息
    @GetMapping
    public ResponseEntity<List<Emp>> getAllEmployees() {
        log.info("Request to get all employees");
        List<Emp> employees = empService.getAllEmployees();
        return ResponseEntity.ok(employees);
    }

    // POST /api/employees - 新增员工
    @PostMapping
    public ResponseEntity<Emp> createEmployee(@RequestBody Emp emp /*, @RequestBody List<Empr> experiences (更复杂场景)*/) {
        log.info("Request to create new employee: {}", emp.getEmpName());
        empService.addNewEmployee(emp /*, experiences*/);
        // 通常在emp对象中会包含数据库生成的ID,如果配置了MyBatis返回主键
        return ResponseEntity.status(HttpStatus.CREATED).body(emp); // 返回201 Created 和创建的员工数据
    }

    // PUT /api/employees/{id} - 更新指定ID的员工信息
    @PutMapping("/{id}")
    public ResponseEntity<?> updateEmployee(@PathVariable Integer id, @RequestBody Emp emp) {
        log.info("Request to update employee with ID: {}", id);
        // 确保emp对象的id与路径变量id一致,或以路径变量为准
        emp.setEmpId(id);
        empService.updateEmployeeInfo(emp);
        return ResponseEntity.ok("Employee updated successfully");
    }

    // DELETE /api/employees/{id} - 删除指定ID的员工
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteEmployee(@PathVariable Integer id) {
        log.info("Request to delete employee with ID: {}", id);
        empService.removeEmployee(id);
        return ResponseEntity.noContent().build(); // 返回204 No Content
    }
}

关键点

  • 依赖注入 (@Autowired):注入 EmpService 实例。
  • 请求映射注解
    • @RequestMapping("/api/employees"): 定义类级别的基础 URL 路径。
    • @GetMapping@PostMapping@PutMapping@DeleteMapping: 分别对应 HTTP 的 GET, POST, PUT, DELETE 请求方法,并可指定具体的子路径 (如 /{id})。
  • 参数绑定注解
    • @PathVariable: 从 URL 路径中提取参数 (如 /{id} 中的 id)。
    • @RequestBody: 将 HTTP 请求体中的 JSON/XML 数据自动转换为 Java 对象 (如 Emp 对象)。
    • @RequestParam: 从 URL 查询参数中提取值 (如 ?name=John)。
  • 响应处理 (ResponseEntity)
    • 使用 ResponseEntity 可以更精细地控制 HTTP 响应,包括状态码、头部信息和响应体。
    • 可以直接返回 POJO 对象,Spring MVC 会自动通过 HttpMessageConverter (如 JacksonHttpMessageConverter) 将其序列化为 JSON (默认) 或 XML。
  • RESTful API 设计:通常遵循 RESTful 风格来设计 API 接口。

总结与强调

  • 分层解耦:每一层都有明确的职责,Controller 负责调度和 HTTP 交互,Service 负责业务逻辑,Mapper 负责数据持久化。这种分离使得各层可以独立开发、测试和修改,降低了模块间的耦合度。
  • Spring IOC (Inversion of Control):通过 @Mapper@Service@RestController 等注解,我们将对象的创建和管理的权力交给了 Spring 容器。我们需要使用某个组件时,通过 @Autowired 进行依赖注入即可,无需手动 new 对象,这简化了对象管理和依赖关系。
  • 面向接口编程:尤其是在 Service 层,通过定义接口和实现类,可以提高系统的灵活性和可扩展性。例如,未来如果需要更换 EmpServiceImpl 的实现,只要新的实现类也实现了 EmpService 接口,Controller 层的代码基本无需改动。
  • 声明式事务:通过在 Service 层方法上使用 @Transactional 注解,可以优雅地实现事务管理,而无需编写冗余的事务控制代码。
  • Lombok (可选但推荐):通过 @Data@Slf4j 等注解,可以减少大量的样板代码,使代码更简洁易读。

这个流程是 Spring Boot Web 应用开发中非常经典和常用的一种实践。它清晰地划分了不同组件的职责,使得项目结构更加规范,易于团队协作和长期维护。

如对内容有异议,请联系关邮箱2285786274@qq.com修改