SpringBoot开发模式 springboot-patterns

Spring Boot开发模式是一种Java后端开发架构,采用分层设计包括控制器、服务、仓库层,实现REST API、JPA持久化和全局异常处理。适用于构建可扩展、可维护的企业级应用,关键词包括Spring Boot、Java后端、架构设计、REST API、JPA、微服务。

后端开发 0 次安装 0 次浏览 更新于 3/8/2026

名称: springboot-patterns 描述: Spring Boot模式包括JPA仓库、REST控制器、分层服务和配置

Spring Boot模式

分层架构

src/main/java/com/example/app/
  config/          # @Configuration beans(配置Bean)
  controller/      # @RestController(薄层,委托给服务)
  service/         # @Service(业务逻辑)
  repository/      # @Repository(通过JPA进行数据访问)
  model/
    entity/        # @Entity JPA类
    dto/           # 请求/响应DTO
    mapper/        # MapStruct或手动映射
  exception/       # @ControllerAdvice,自定义异常
  security/        # SecurityFilterChain,JWT过滤器

控制器处理HTTP相关事宜。服务包含业务逻辑。仓库处理持久化。

REST控制器

@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;

    @GetMapping
    public Page<OrderResponse> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        return orderService.findAll(PageRequest.of(page, size));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public OrderResponse create(@Valid @RequestBody CreateOrderRequest request) {
        return orderService.create(request);
    }

    @GetMapping("/{id}")
    public OrderResponse getById(@PathVariable UUID id) {
        return orderService.findById(id);
    }
}

JPA实体和仓库

@Entity
@Table(name = "orders")
@Getter @Setter @NoArgsConstructor
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer_id", nullable = false)
    private Customer customer;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();

    @Enumerated(EnumType.STRING)
    private OrderStatus status = OrderStatus.PENDING;

    @CreationTimestamp
    private Instant createdAt;
}

public interface OrderRepository extends JpaRepository<Order, UUID> {
    @Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.customer.id = :customerId")
    List<Order> findByCustomerWithItems(@Param("customerId") UUID customerId);

    @EntityGraph(attributePaths = {"customer", "items"})
    Optional<Order> findWithDetailsById(UUID id);
}

服务层

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;
    private final OrderMapper orderMapper;
    private final EventPublisher eventPublisher;

    public OrderResponse findById(UUID id) {
        Order order = orderRepository.findWithDetailsById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Order", id));
        return orderMapper.toResponse(order);
    }

    @Transactional
    public OrderResponse create(CreateOrderRequest request) {
        Order order = orderMapper.toEntity(request);
        order = orderRepository.save(order);
        eventPublisher.publish(new OrderCreatedEvent(order.getId()));
        return orderMapper.toResponse(order);
    }
}

全局异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ProblemDetail handleNotFound(ResourceNotFoundException ex) {
        return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
        ProblemDetail detail = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
        Map<String, String> errors = ex.getFieldErrors().stream()
                .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
        detail.setProperty("errors", errors);
        return detail;
    }
}

反模式

  • 直接将仓库注入控制器(绕过服务层)
  • 默认在实体关系上使用FetchType.EAGER
  • 从控制器直接返回JPA实体而不是DTO
  • 在只读服务方法上缺少@Transactional(readOnly = true)
  • 捕获通用Exception而不是特定类型
  • 硬编码配置值而不是使用@Value@ConfigurationProperties

检查清单

  • [ ] 控制器是薄层并委托给服务
  • [ ] 所有JPA关系默认使用FetchType.LAZY
  • [ ] 请求/响应使用DTO,从不使用原始实体
  • [ ] 在服务级别应用@Transactional,具有正确的读/写范围
  • [ ] 请求DTO上有验证注解(@Valid@NotNull@Size
  • [ ] 全局异常处理返回ProblemDetail(RFC 7807)
  • [ ] 使用实体图或JOIN FETCH避免N+1查询
  • [ ] 集成测试使用@SpringBootTest与测试容器