Spring Boot 分层架构设计
原创2024/1/23大约 5 分钟
Spring Boot 分层架构设计
Spring Boot 采用经典的分层架构,将应用分为不同的逻辑层,每层负责特定的职责。
Spring Boot 分层架构图
上图展示了标准的 Spring Boot 四层架构,让我们详细了解每一层的职责和实现。
Controller 层 (表现层)
Controller 层负责处理 HTTP 请求,是应用的入口。
基本示例
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<UserDTO> createUser(
@Valid @RequestBody CreateUserRequest request
) {
UserDTO user = userService.createUser(request);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(user);
}
@PutMapping("/{id}")
public ResponseEntity<UserDTO> updateUser(
@PathVariable Long id,
@Valid @RequestBody UpdateUserRequest request
) {
UserDTO user = userService.updateUser(id, request);
return ResponseEntity.ok(user);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}Controller 职责
- ✅ 接收 HTTP 请求
- ✅ 参数验证
- ✅ 调用 Service 层
- ✅ 返回 HTTP 响应
- ❌ 不包含业务逻辑
- ❌ 不直接访问数据库
请求参数验证
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
@NotBlank(message = "密码不能为空")
@Pattern(
regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$",
message = "密码必须至少8位,包含字母和数字"
)
private String password;
// Getters and Setters
}Service 层 (业务层)
Service 层包含核心业务逻辑,是应用的大脑。
基本示例
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final EmailService emailService;
@Autowired
public UserService(
UserRepository userRepository,
PasswordEncoder passwordEncoder,
EmailService emailService
) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.emailService = emailService;
}
public UserDTO getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
return UserMapper.toDTO(user);
}
public UserDTO createUser(CreateUserRequest request) {
// 检查用户是否已存在
if (userRepository.existsByEmail(request.getEmail())) {
throw new UserAlreadyExistsException(request.getEmail());
}
// 创建用户对象
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setCreatedAt(LocalDateTime.now());
// 保存用户
User savedUser = userRepository.save(user);
// 发送欢迎邮件 (异步)
emailService.sendWelcomeEmail(savedUser.getEmail());
return UserMapper.toDTO(savedUser);
}
public void deleteUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
// 业务规则:不能删除管理员
if (user.isAdmin()) {
throw new CannotDeleteAdminException();
}
userRepository.delete(user);
}
}Service 职责
- ✅ 实现业务逻辑
- ✅ 事务管理
- ✅ 数据转换 (Entity ↔ DTO)
- ✅ 调用 Repository 层
- ✅ 业务规则验证
- ❌ 不处理 HTTP 请求
- ❌ 不直接操作数据库
事务管理
@Service
public class OrderService {
@Transactional
public Order createOrder(CreateOrderRequest request) {
// 创建订单
Order order = orderRepository.save(new Order(request));
// 减少库存
productService.decreaseStock(request.getProductId(), request.getQuantity());
// 扣除余额
walletService.deduct(request.getUserId(), order.getTotalAmount());
// 发送通知
notificationService.sendOrderConfirmation(order);
return order;
// 任何步骤失败都会回滚整个事务
}
}Repository 层 (持久层)
Repository 层负责数据访问,使用 Spring Data JPA 简化 CRUD 操作。
基本示例
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 方法名查询
Optional<User> findByEmail(String email);
Optional<User> findByUsername(String username);
boolean existsByEmail(String email);
// @Query 注解
@Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
List<User> findByEmailDomain(@Param("domain") String domain);
@Query("SELECT u FROM User u WHERE u.createdAt BETWEEN :start AND :end")
List<User> findUsersCreatedBetween(
@Param("start") LocalDateTime start,
@Param("end") LocalDateTime end
);
// 原生 SQL
@Query(value = "SELECT * FROM users WHERE status = :status",
nativeQuery = true)
List<User> findByStatusNative(@Param("status") String status);
// 更新操作
@Modifying
@Query("UPDATE User u SET u.lastLoginAt = :time WHERE u.id = :id")
void updateLastLoginTime(
@Param("id") Long id,
@Param("time") LocalDateTime time
);
}Repository 职责
- ✅ 数据库 CRUD 操作
- ✅ 复杂查询
- ✅ 数据持久化
- ❌ 不包含业务逻辑
- ❌ 不进行数据转换
自定义 Repository
public interface CustomUserRepository {
List<User> findUsersByComplexCriteria(UserSearchCriteria criteria);
}
@Repository
public class CustomUserRepositoryImpl implements CustomUserRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<User> findUsersByComplexCriteria(UserSearchCriteria criteria) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (criteria.getUsername() != null) {
predicates.add(cb.like(root.get("username"),
"%" + criteria.getUsername() + "%"));
}
if (criteria.getMinAge() != null) {
predicates.add(cb.ge(root.get("age"), criteria.getMinAge()));
}
query.where(predicates.toArray(new Predicate[0]));
return entityManager.createQuery(query).getResultList();
}
}实体层 (Entity)
Entity 是数据库表的映射。
@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private UserRole role = UserRole.USER;
@CreatedDate
@Column(nullable = false, updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
// Getters and Setters
}DTO (数据传输对象)
DTO 用于层与层之间传输数据。
public class UserDTO {
private Long id;
private String username;
private String email;
private UserRole role;
private LocalDateTime createdAt;
// 不包含密码等敏感信息
// Getters and Setters
}
// Mapper
public class UserMapper {
public static UserDTO toDTO(User user) {
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setUsername(user.getUsername());
dto.setEmail(user.getEmail());
dto.setRole(user.getRole());
dto.setCreatedAt(user.getCreatedAt());
return dto;
}
public static User toEntity(UserDTO dto) {
User user = new User();
user.setId(dto.getId());
user.setUsername(dto.getUsername());
user.setEmail(dto.getEmail());
user.setRole(dto.getRole());
return user;
}
}异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(
UserNotFoundException ex
) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidException ex
) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
}分层架构优势
- 关注点分离: 每层只关注自己的职责
- 可维护性: 修改某层不影响其他层
- 可测试性: 每层可以独立测试
- 可扩展性: 容易添加新功能
- 团队协作: 不同层可以并行开发
最佳实践
- 依赖注入: 使用构造器注入
- 面向接口编程: Service 层定义接口
- DTO 转换: Entity 不直接暴露给外部
- 事务边界: 在 Service 层管理事务
- 异常处理: 统一的异常处理机制
总结
Spring Boot 的分层架构清晰、规范,是构建企业级应用的最佳实践。理解每层的职责,能够写出高质量、可维护的代码。