肥仔教程网

SEO 优化与 Web 开发技术学习分享平台

Spring Boot 解耦开发(springboot解析pom)

一个完整的Spring Boot分层架构代码示例

  • UserDTO:前端传参对象
  • UserVO:返回给前端的对象
  • UserEntity:数据库实体
  • UserMapper:用于 DTO/VO 与 Entity 之间转换
  • UserService / UserServiceImpl:业务逻辑层
  • UserRepository:数据访问层(JPA)
  • UserController:接口控制层

在这个示例代码中:

  • 使用 DTO 解耦前端请求结构与数据库结构;
  • 使用 VO 精简和安全化返回前端的数据;
  • Mapper 保持各层之间的“清洁边界”;
  • 各层职责单一清晰:Service 聚焦业务,Repository 聚焦数据操作,Controller 聚焦交互;
  • 高度可扩展和可维护的代码结构

项目结构

src
├── controller
│   └── UserController.java
├── dto
│   └── UserDTO.java
├── vo
│   └── UserVO.java
├── entity
│   └── UserEntity.java
├── mapper
│   └── UserMapper.java
├── repository
│   └── UserRepository.java
├── service
│   ├── UserService.java
│   └── impl
│       └── UserServiceImpl.java

UserDTO.java

public class UserDTO {
    private String username;
    private String email;

    // Getters and Setters
}

UserVO.java

public class UserVO {
    private Long id;
    private String username;
    private String status;

    // Getters and Setters
}

UserEntity.java

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String email;

    private String status; // e.g. ACTIVE / INACTIVE

    // Getters and Setters
}

UserMapper.java

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring")
public interface UserMapper {

    UserEntity dtoToEntity(UserDTO dto);

    @Mapping(source = "id", target = "id")
    @Mapping(source = "username", target = "username")
    @Mapping(source = "status", target = "status")
    UserVO entityToVO(UserEntity entity);
}

因为字段名一致,所以UserVOentityToVO(UserEntity entity);上面其实是不需要写@Mapping的。

UserRepository.java

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<UserEntity, Long> {
    boolean existsByUsername(String username);
}

这里的existsByUsername 是一个知识点:Spring Data JPA 的“方法名派生查询”功能 - 知乎

UserService.java

import java.util.List;

public interface UserService {
    UserEntity createUser(UserEntity user);
    List<UserEntity> getAllUsers();
}

UserServiceImpl.java

import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserEntity createUser(UserEntity user) {
        user.setStatus("ACTIVE");
        return userRepository.save(user);
    }

    @Override
    public List<UserEntity> getAllUsers() {
        return userRepository.findAll();
    }
}

不建议在private final UserRepository userRepository上方加上@Autowired,为了便捷不去写构造函数。

UserController.java

import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;
    private final UserMapper userMapper;

    public UserController(UserService userService, UserMapper userMapper) {
        this.userService = userService;
        this.userMapper = userMapper;
    }

    @PostMapping
    public UserVO createUser(@RequestBody UserDTO userDTO) {
        UserEntity entity = userMapper.dtoToEntity(userDTO);
        UserEntity saved = userService.createUser(entity);
        return userMapper.entityToVO(saved);
    }

    @GetMapping
    public List<UserVO> getAllUsers() {
        // 获取所有用户实体
        List<UserEntity> userEntities = userService.getAllUsers();
        
        // 创建返回结果列表
        List<UserVO> userVOs = new ArrayList<>();
        
        // 遍历转换每个实体为VO
        for (UserEntity entity : userEntities) {
            UserVO vo = userMapper.entityToVO(entity);
            userVOs.add(vo);
        }
        
        return userVOs;
    }
}

示例请求与返回

创建用户(POST /api/users):

  • 请求体:
Request Body:
{
  "username": "alice",
  "email": "alice@example.com"
}
  • 响应体:
Response:
{
  "id": 1,
  "username": "alice",
  "status": "ACTIVE"
}

获取所有用户(GET /api/users):

  • GET请求无请求体
  • 响应体:
[
  {
    "id": 1,
    "username": "alice",
    "status": "ACTIVE"
  },
  {
    "id": 2,
    "username": "bob",
    "status": "INACTIVE"
  },
  // 更多用户...
]

数据流转路径

在上述代码中,数据流转路径为:

sequenceDiagram
    participant Frontend as 前端
    participant Controller as Controller 控制层
    participant Mapper as Mapper 映射器
    participant Service as Service 业务层
    participant Repository as Repository 数据访问层
    participant DB as 数据库

    %% 前端请求提交
    Frontend->>Controller: 1. 提交 JSON 请求(UserDTO)

    %% 控制器调用 Mapper
    Controller->>Mapper: 2. DTO → Entity
    Mapper-->>Controller: 3. 返回 Entity

    %% 控制器调用服务层
    Controller->>Service: 4. 调用 createUser(Entity)

    %% 服务层、数据访问层处理
    Service->>Repository: 5. save(Entity)
    Repository->>DB: 6. 执行 INSERT
    DB-->>Repository: 7. 返回保存结果
    Repository-->>Service: 8. 返回 Entity
    Service-->>Controller: 9. 返回 Entity

    %% Controller 映射 VO
    Controller->>Mapper: 10. Entity → VO
    Mapper-->>Controller: 11. 返回 UserVO

    %% 响应给前端
    Controller-->>Frontend: 12. 响应 UserVO

mermaid语言画的图

  1. 请求路径: 前端 → Controller(DTO) → Mapper → Service(Entity) → Repository → DB
  2. 响应路径: DB → Repository(Entity) → Service → Controller → Mapper(VO) → 前端
  3. 转换节点
    入口:DTO → Entity (通过Mapper)
    出口:Entity → VO (通过Mapper)

使用DTO/VO可以解耦,具体体现在哪些方面?

解耦体现的关键价值

层次

解耦点

说明

数据 → 请求

DTO

防止前端影响数据库字段

数据 → 响应

VO

防止数据库字段暴露

Controller → Service

使用结构体封装

参数/返回值清晰,接口定义独立

数据库表 → 系统结构

Entity 封装底层结构

改表不改业务模型

使用DTO/VO与不使用DTO/VO的对比

不使用 DTO / VO(紧耦合):

public class User {
    private Long id;
    private String username;
    private String password; // 不应该返回给前端
    private Timestamp createdAt;
}
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
    return userRepository.findById(id).orElse(null);
}

出现的问题:

  • 前端能拿到 password
  • 表结构改了(比如换成 hashedPassword),前端也会被影响;
  • Entity 里如果加入展示逻辑(如时间格式化),违反职责单一。

使用 DTO + VO(松耦合):

// 数据库映射用 Entity
public class UserEntity {
    private Long id;
    private String username;
    private String password;
    private Timestamp createdAt;
}

// 接收前端注册数据
public class UserRegisterDTO {
    private String username;
    private String password;
}

// 返回前端展示数据
public class UserVO {
    private String username;
    private String createdTime; // 格式化后的时间
}

好处:

  • 前端永远不会知道你数据库里字段长啥样;
  • Entity 可以自由改结构,VO 只要字段名一致就不会受影响;
  • 可以根据前端需求增加字段而不动数据库结构。

DTO / VO 使用建议

总结:除了 GET,一般所有修改类请求(POST / PUT / PATCH)都应该使用 DTO 入参、VO 出参,目的是“结构清晰 + 安全解耦”。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言