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

 

 

十二、功能验证方式

  1. 接口文档:启动后访问 http://localhost:8080/swagger-ui.html
  2. 提交留言:POST /api/contact,传参错误会触发校验异常,返回统一格式
  3. 审计验证:提交成功后查看数据库,createdAtcreatedBy 自动填充
  4. 作用域测试:访问 http://localhost:8080/api/scope/test,多刷新、换浏览器观察计数变化

 

 

posted @ 2026-08-01 00:58  漫漫长路</>  阅读(6)  评论(0)    收藏  举报