mcpField
S
springboot-patterns

springboot-patterns

github.com/rohitg00开发2.8k189 次安装25 周前更新
代码仓库

关于 springboot-patterns

面向 Spring Boot 开发者的模式指南,介绍 Java 分层结构、REST 与持久化实践、事务处理、错误响应及测试检查项。

主要功能

  1. 01分层应用结构
  2. 02REST 控制器与 DTO
  3. 03JPA 实体和 Repository 指引
  4. 04服务层事务实践
  5. 05异常处理与测试清单

适用场景

  1. 01规划 Spring Boot 应用结构
  2. 02检查 REST 与持久化代码
  3. 03核对事务和异常处理实践

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rohitg00/awesome-claude-code-toolkit --skill springboot-patterns

安装后在 Claude Code 中输入/springboot-patterns确认已加载。

详细介绍

Spring Boot 模式

分层架构

src/main/java/com/example/app/
  config/          # @Configuration beans
  controller/      # @RestController (thin, delegates to service)
  service/         # @Service (business logic)
  repository/      # @Repository (data access via JPA)
  model/
    entity/        # @Entity JPA classes
    dto/           # Request/response DTOs
    mapper/        # MapStruct or manual mapping
  exception/       # @ControllerAdvice, custom exceptions
  security/        # SecurityFilterChain, JWT filters

控制器处理 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 和测试容器

评论

支持 Markdown