spring boot 全局异常处理 + 后端参数校验 + JPA 审计 + Swagger 接口文档 + Bean 作用域---8
覆盖:全局异常处理 + 后端参数校验 + JPA 审计 + Swagger 接口文档 + Bean 作用域,全部整合进你的
jobportal 项目,包结构统一,可直接复制启动。一、整体项目结构
plaintext
com.example.jobportal ├── JobportalApplication.java # 启动类 ├── common │ └── Result.java # 统一响应结果 ├── exception │ ├── BusinessException.java # 自定义业务异常 │ └── GlobalExceptionHandler.java # 全局异常处理器 ├── dto │ └── ContactDTO.java # 接收参数+校验 ├── entity │ └── Contact.java # 数据库实体(带审计) ├── repository │ └── ContactRepository.java ├── service │ └── ContactService.java ├── controller │ ├── ContactController.java # 联系我们接口 │ └── ScopeTestController.java # 作用域测试接口 ├── config │ ├── AuditorConfig.java # JPA审计配置 │ └── OpenApiConfig.java # Swagger配置 └── scope ├── RequestScopeBean.java ├── SessionScopeBean.java └── ApplicationScopeBean.java
二、pom.xml 完整依赖
xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>4.0.0</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>jobportal</artifactId> <version>0.0.1-SNAPSHOT</version> <name>jobportal</name> <properties> <java.version>17</java.version> </properties> <dependencies> <!-- Web核心 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- 参数校验 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- Lombok 简化代码 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- Swagger / OpenAPI 接口文档 --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.5.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
三、公共模块
1. 统一响应结果 common/Result.java
java
运行
package com.example.jobportal.common; import lombok.Data; import java.time.LocalDateTime; @Data public class Result<T> { private int code; private String message; private T data; private LocalDateTime timestamp; private Result() { this.timestamp = LocalDateTime.now(); } public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("操作成功"); result.setData(data); return result; } public static <T> Result<T> fail(int code, String message) { Result<T> result = new Result<>(); result.setCode(code); result.setMessage(message); return result; } }
四、全局异常处理(对应 88、89 集)
1. 自定义业务异常 exception/BusinessException.java
java
运行
package com.example.jobportal.exception; import lombok.Getter; @Getter public class BusinessException extends RuntimeException { private final int code; public BusinessException(String message) { this(400, message); } public BusinessException(int code, String message) { super(message); this.code = code; } }
2. 全局异常处理器 exception/GlobalExceptionHandler.java
java
运行
package com.example.jobportal.exception; import com.example.jobportal.common.Result; import org.springframework.http.HttpStatus; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.stream.Collectors; @RestControllerAdvice public class GlobalExceptionHandler { // 处理自定义业务异常 @ExceptionHandler(BusinessException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result<Void> handleBusinessException(BusinessException e) { return Result.fail(e.getCode(), e.getMessage()); } // 处理参数校验失败异常 @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result<Void> handleValidException(MethodArgumentNotValidException e) { String msg = e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", ")); return Result.fail(400, "参数错误:" + msg); } // 兜底:系统异常 @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public Result<Void> handleException(Exception e) { e.printStackTrace(); return Result.fail(500, "系统内部错误,请稍后重试"); } }
五、后端参数校验(对应 90~93 集)
DTO 接收参数 dto/ContactDTO.java
java
运行
package com.example.jobportal.dto; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import lombok.Data; @Data public class ContactDTO { @NotBlank(message = "姓名不能为空") @Size(min = 2, max = 50, message = "姓名长度必须在 2-50 之间") private String name; @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不正确") private String email; @NotBlank(message = "留言内容不能为空") @Size(max = 500, message = "留言不能超过 500 字") private String message; }
六、JPA 审计功能(对应 94、95 集)
1. 实体类 entity/Contact.java
java
运行
package com.example.jobportal.entity; import jakarta.persistence.*; import lombok.Data; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.time.LocalDateTime; @Data @Entity @Table(name = "contact_messages") @EntityListeners(AuditingEntityListener.class) public class Contact { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; @Column(columnDefinition = "TEXT") private String message; // ===== 自动填充审计字段 ===== @CreatedDate @Column(updatable = false) private LocalDateTime createdAt; @CreatedBy @Column(updatable = false) private String createdBy; @LastModifiedDate private LocalDateTime updatedAt; @LastModifiedBy private String updatedBy; }
2. 审计操作人配置 config/AuditorConfig.java
java
运行
package com.example.jobportal.config; import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.AuditorAware; import java.util.Optional; @Configuration public class AuditorConfig implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { // 后续接入Spring Security后替换为真实登录用户名 return Optional.of("system"); } }
3. Repository 接口 repository/ContactRepository.java
java
运行
package com.example.jobportal.repository; import com.example.jobportal.entity.Contact; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface ContactRepository extends JpaRepository<Contact, Long> { }
七、Service 层
java
运行
package com.example.jobportal.service; import com.example.jobportal.dto.ContactDTO; import com.example.jobportal.entity.Contact; import com.example.jobportal.exception.BusinessException; import com.example.jobportal.repository.ContactRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class ContactService { private final ContactRepository contactRepository; public Contact saveContact(ContactDTO dto) { // 示例:业务校验,重复提交拦截 if (dto.getMessage().contains("测试垃圾内容")) { throw new BusinessException("请勿提交违规内容"); } Contact contact = new Contact(); contact.setName(dto.getName()); contact.setEmail(dto.getEmail()); contact.setMessage(dto.getMessage()); return contactRepository.save(contact); } }
八、Controller 层(含 Swagger 注解,对应 96 集)
java
运行
package com.example.jobportal.controller; import com.example.jobportal.common.Result; import com.example.jobportal.dto.ContactDTO; import com.example.jobportal.entity.Contact; import com.example.jobportal.service.ContactService; 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.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/contact") @RequiredArgsConstructor @Tag(name = "联系我们接口", description = "用户提交留言反馈相关接口") public class ContactController { private final ContactService contactService; @PostMapping @Operation(summary = "提交留言", description = "用户填写信息后提交联系留言,存入数据库") public Result<Contact> submit(@Valid @RequestBody ContactDTO dto) { Contact contact = contactService.saveContact(dto); return Result.success(contact); } }
Swagger 配置类 config/OpenApiConfig.java
java
运行
package com.example.jobportal.config; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class OpenApiConfig { @Bean public OpenAPI jobPortalOpenAPI() { return new OpenAPI() .info(new Info() .title("JobPortal 招聘平台 API") .version("1.0") .description("联系我们、岗位管理等后端接口文档")); } }
九、Bean 作用域(对应 97~99 集)
1. Request 作用域 scope/RequestScopeBean.java
java
运行
package com.example.jobportal.scope; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; @Component @RequestScope public class RequestScopeBean { private int count = 0; public int addAndGet() { count++; return count; } }
2. Session 作用域 scope/SessionScopeBean.java
java
运行
package com.example.jobportal.scope; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.SessionScope; @Component @SessionScope public class SessionScopeBean { private int count = 0; public int addAndGet() { count++; return count; } }
3. Application 作用域 scope/ApplicationScopeBean.java
java
运行
package com.example.jobportal.scope; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.ApplicationScope; @Component @ApplicationScope public class ApplicationScopeBean { private int count = 0; public int addAndGet() { count++; return count; } }
4. 作用域测试 Controller
java
运行
package com.example.jobportal.controller; import com.example.jobportal.scope.ApplicationScopeBean; import com.example.jobportal.scope.RequestScopeBean; import com.example.jobportal.scope.SessionScopeBean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/api/scope") public class ScopeTestController { private final RequestScopeBean requestScopeBean; private final SessionScopeBean sessionScopeBean; private final ApplicationScopeBean applicationScopeBean; public ScopeTestController(RequestScopeBean requestScopeBean, SessionScopeBean sessionScopeBean, ApplicationScopeBean applicationScopeBean) { this.requestScopeBean = requestScopeBean; this.sessionScopeBean = sessionScopeBean; this.applicationScopeBean = applicationScopeBean; } @GetMapping("/test") public Map<String, Integer> testScope() { Map<String, Integer> result = new HashMap<>(); result.put("request作用域计数", requestScopeBean.addAndGet()); result.put("session作用域计数", sessionScopeBean.addAndGet()); result.put("application作用域计数", applicationScopeBean.addAndGet()); return result; } }
十、启动类
java
运行
package com.example.jobportal; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableJpaAuditing // 开启JPA审计 public class JobportalApplication { public static void main(String[] args) { SpringApplication.run(JobportalApplication.class, args); } }
十一、application.properties 完整配置
properties
spring.application.name=jobportal # 数据源配置 spring.datasource.url=jdbc:mysql://localhost:3306/jobportal?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8mb4&useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # JPA配置 spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect # Swagger路径(可选) springdoc.api-docs.path=/api-docs springdoc.swagger-ui.path=/swagger-ui.html
十二、功能验证方式
- 接口文档:启动后访问
http://localhost:8080/swagger-ui.html - 提交留言:POST
/api/contact,传参错误会触发校验异常,返回统一格式 - 审计验证:提交成功后查看数据库,
createdAt、createdBy自动填充 - 作用域测试:访问
http://localhost:8080/api/scope/test,多刷新、换浏览器观察计数变化

浙公网安备 33010602011771号