Spring Boot 4 开发规范
本项目使用 Spring Boot 4.0.1 + JPA + MySQL 8 技术栈。
技术栈
技术 版本 用途
Spring Boot 4.0.1 Web 框架
Spring Data JPA 3.4.x 数据访问
Hibernate 7.x ORM
MySQL 8.0 数据库
JSpecify 1.0.0 Null 安全
SpringDoc 2.8.x API 文档
Lombok
简化代码
目录结构
backend/src/main/java/com/taichu/yingjiguanli/ ├── YingjiGuanliApplication.java # 启动类 ├── common/ # 通用类 │ ├── ApiResponse.java # 统一响应 │ ├── BusinessException.java # 业务异常 │ └── PageResult.java # 分页结果 ├── config/ # 配置类 │ ├── CorsConfig.java │ ├── GlobalExceptionHandler.java │ └── JpaConfig.java ├── modules/ # 业务模块 │ └── {module}/ # 模块名 │ ├── entity/ # 实体类 │ ├── repository/ # 数据访问 │ ├── service/ # 服务层 │ │ └── impl/ │ ├── controller/ # 控制器 │ ├── dto/ # 数据传输对象 │ └── vo/ # 视图对象 └── resources/ ├── application.yml └── db/migration/ # Flyway 迁移
代码模板
Entity 实体类
package com.taichu.yingjiguanli.modules.{module}.entity;
import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.jspecify.annotations.Nullable;
import java.time.LocalDateTime;
/**
-
{实体描述}
-
@author CX
-
@since {date} */ @Data @Entity @Table(name = "{table_name}") @Comment("{表描述}") public class {EntityName} {
/**
- 主键ID */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Comment("主键ID") private Long id;
/**
- {字段描述} */ @Column(nullable = false, length = 100) @Comment("{字段描述}") private String name;
/**
- 创建时间 */ @Column(nullable = false, updatable = false) @Comment("创建时间") private LocalDateTime createdAt;
/**
- 更新时间 */ @Column(nullable = false) @Comment("更新时间") private LocalDateTime updatedAt;
@PrePersist protected void onCreate() { createdAt = LocalDateTime.now(); updatedAt = LocalDateTime.now(); }
@PreUpdate protected void onUpdate() { updatedAt = LocalDateTime.now(); } }
Repository 数据访问
package com.taichu.yingjiguanli.modules.{module}.repository;
import com.taichu.yingjiguanli.modules.{module}.entity.{EntityName}; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
-
{实体}数据访问接口
-
@author CX
-
@since {date} */ @Repository public interface {EntityName}Repository extends JpaRepository<{EntityName}, Long>, JpaSpecificationExecutor<{EntityName}> {
/**
- 根据名称查询 */ Optional<{EntityName}> findByName(String name); }
Service 服务层
package com.taichu.yingjiguanli.modules.{module}.service;
import com.taichu.yingjiguanli.modules.{module}.dto.{EntityName}DTO; import com.taichu.yingjiguanli.modules.{module}.entity.{EntityName}; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable;
import java.util.Optional;
/**
-
{实体}服务接口
-
@author CX
-
@since {date} */ public interface {EntityName}Service {
/**
- 创建 */ {EntityName} create({EntityName}DTO dto);
/**
- 更新 */ {EntityName} update(Long id, {EntityName}DTO dto);
/**
- 删除 */ void delete(Long id);
/**
- 根据ID查询 */ Optional<{EntityName}> findById(Long id);
/**
- 分页查询 */ Page<{EntityName}> findAll(Pageable pageable); }
ServiceImpl 服务实现
package com.taichu.yingjiguanli.modules.{module}.service.impl;
import com.taichu.yingjiguanli.common.BusinessException; import com.taichu.yingjiguanli.modules.{module}.dto.{EntityName}DTO; import com.taichu.yingjiguanli.modules.{module}.entity.{EntityName}; import com.taichu.yingjiguanli.modules.{module}.repository.{EntityName}Repository; import com.taichu.yingjiguanli.modules.{module}.service.{EntityName}Service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
/**
-
{实体}服务实现
-
@author CX
-
@since {date} */ @Slf4j @Service @RequiredArgsConstructor public class {EntityName}ServiceImpl implements {EntityName}Service {
private final {EntityName}Repository repository;
@Override @Transactional public {EntityName} create({EntityName}DTO dto) { log.info("创建{实体}: {}", dto); {EntityName} entity = new {EntityName}(); // 设置属性... return repository.save(entity); }
@Override @Transactional public {EntityName} update(Long id, {EntityName}DTO dto) { log.info("更新{实体} ID={}: {}", id, dto); {EntityName} entity = repository.findById(id) .orElseThrow(() -> new BusinessException(404, "{实体}不存在")); // 更新属性... return repository.save(entity); }
@Override @Transactional public void delete(Long id) { log.info("删除{实体} ID={}", id); if (!repository.existsById(id)) { throw new BusinessException(404, "{实体}不存在"); } repository.deleteById(id); }
@Override public Optional<{EntityName}> findById(Long id) { return repository.findById(id); }
@Override public Page<{EntityName}> findAll(Pageable pageable) { return repository.findAll(pageable); } }
Controller 控制器
package com.taichu.yingjiguanli.modules.{module}.controller;
import com.taichu.yingjiguanli.common.ApiResponse; import com.taichu.yingjiguanli.common.BusinessException; import com.taichu.yingjiguanli.modules.{module}.dto.{EntityName}DTO; import com.taichu.yingjiguanli.modules.{module}.entity.{EntityName}; import com.taichu.yingjiguanli.modules.{module}.service.{EntityName}Service; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.web.bind.annotation.*;
/**
-
{实体}控制器
-
@author CX
-
@since {date} */ @RestController @RequestMapping("/api/{module}") @RequiredArgsConstructor @Tag(name = "{模块名称}", description = "{模块描述}") public class {EntityName}Controller {
private final {EntityName}Service service;
/**
- 创建 */ @PostMapping @Operation(summary = "创建{实体}") public ApiResponse<{EntityName}> create(@Valid @RequestBody {EntityName}DTO dto) { return ApiResponse.success(service.create(dto)); }
/**
- 更新 */ @PutMapping("/{id}") @Operation(summary = "更新{实体}") public ApiResponse<{EntityName}> update(@PathVariable Long id, @Valid @RequestBody {EntityName}DTO dto) { return ApiResponse.success(service.update(id, dto)); }
/**
- 删除 */ @DeleteMapping("/{id}") @Operation(summary = "删除{实体}") public ApiResponse<Void> delete(@PathVariable Long id) { service.delete(id); return ApiResponse.success(); }
/**
- 查询详情 */ @GetMapping("/{id}") @Operation(summary = "查询{实体}详情") public ApiResponse<{EntityName}> getById(@PathVariable Long id) { return ApiResponse.success(service.findById(id) .orElseThrow(() -> new BusinessException(404, "{实体}不存在"))); }
/**
- 分页查询 */ @GetMapping @Operation(summary = "分页查询{实体}") public ApiResponse<Page<{EntityName}>> list(Pageable pageable) { return ApiResponse.success(service.findAll(pageable)); } }
DTO 数据传输对象
package com.taichu.yingjiguanli.modules.{module}.dto;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import lombok.Data;
/**
-
{实体}数据传输对象
-
@author CX
-
@since {date} */ @Data public class {EntityName}DTO {
/**
- 名称 */ @NotBlank(message = "名称不能为空") @Size(max = 100, message = "名称长度不能超过100") private String name;
// 其他字段... }
Flyway 迁移脚本
-- V{n}__{description}.sql -- 作者: CX -- 日期: {date} -- 描述: {description}
CREATE TABLE {table_name} ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', name VARCHAR(100) NOT NULL COMMENT '名称', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='{表描述}';
命名规范
位置 规范 示例
包名 小写 com.taichu.yingjiguanli.modules.user
类名 PascalCase UserController , UserService
方法名 camelCase findById , createUser
变量名 camelCase userName , createdAt
常量名 UPPER_SNAKE MAX_PAGE_SIZE
表名 snake_case sys_user , t_order
字段名 snake_case user_name , created_at
最佳实践
-
统一响应: 所有接口返回 ApiResponse<T>
-
业务异常: 使用 BusinessException 抛出业务错误
-
参数校验: 使用 @Valid
- jakarta.validation
-
日志记录: 关键操作使用 @Slf4j 记录日志
-
事务管理: Service 层使用 @Transactional
-
中文注释: 所有类、方法、字段必须有中文注释
项目: 应急管理系统 (yingjiguanli) 技术栈: Spring Boot 4.0.1 + JPA + MySQL 8