springboot,全局异常处理,AOP,对所有地方不单单是controller,捕获后事务回滚,但是保存异常发生记录

springboot,全局异常处理,AOP,对所有地方不单单是controller,捕获后事务回滚,但是保存异常发生记录

背景:

业务场景是对外接口调用失败,或者部分代码块执行失败,在系统中能统一记录下来。

方案

  1. 自定义异常
  2. 自定义注解
  3. 编写切面
    在切面中,将异常信息记录到表中。

1.自定义异常

java 复制代码
public class OutApiException extends RuntimeException{
    private String exceptionType;
    private String exceptionInfo;
    private String exceptionParam;
    public OutApiException(String message) {
        super(message);
    }

    public OutApiException(String exceptionType,String exceptionInfo,String exceptionParam,String message) {
        super(message);
        this.exceptionType = exceptionType;
        this.exceptionInfo = exceptionInfo;
        this.exceptionParam = exceptionParam;
    }

    public String getExceptionType() {
        return exceptionType;
    }

    public void setExceptionType(String exceptionType) {
        this.exceptionType = exceptionType;
    }

    public String getExceptionInfo() {
        return exceptionInfo;
    }

    public void setExceptionInfo(String exceptionInfo) {
        this.exceptionInfo = exceptionInfo;
    }

    public String getExceptionParam() {
        return exceptionParam;
    }

    public void setExceptionParam(String exceptionParam) {
        this.exceptionParam = exceptionParam;
    }
}

2.自定义注解

java 复制代码
import java.lang.annotation.*;

@Documented
@Target({ElementType.METHOD, ElementType.TYPE}) //可在类或者方法使用
@Retention(RetentionPolicy.RUNTIME)
public @interface OutApiServiceExceptionCatch {
}

3.切面编写

切面中那么多的代码,其实就是捕获对应注解下的,指定异常

其他的代码是我要存储到表中

为什么用jdbc?

因为我的项目用的mybatis,但是mybatis事务对运行时一行会全部回滚。

所有我单开了一个事务,这样其他的事务回滚了,但是异常可以正常存表。

cpp 复制代码
import com.qygx.framework.exe.OutApiException;
import com.qygx.framework.exe.OutApiServiceExceptionCatch;
import com.qygx.mes.ding.domain.OaTask;
import com.qygx.mes.ding.mapper.OaTaskMapper;
import com.qygx.mes.ding.service.IOaTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import springfox.documentation.service.ResponseMessage;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

@Component
@Aspect
@Slf4j
public class ServiceExceptionHandler {
    @Autowired
    private IOaTaskService oaTaskService;
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    @Autowired
    private DataSource dataSource;
    @Around("@annotation(com.qygx.framework.exe.OutApiServiceExceptionCatch)  || @within(com.qygx.framework.exe.OutApiServiceExceptionCatch)")
    public ResponseEntity<String> serviceExceptionHandler(ProceedingJoinPoint proceedingJoinPoint) {
        ResponseMessage returnMsg;
        try {
            returnMsg = (ResponseMessage) proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("ServiceExcepHandler serviceExcepHandler failed", throwable);
            //单独处理缺少参数异常
            if(throwable instanceof OutApiException) {
                Connection conn = null;
                try {
                    // 从数据源获取连接,并设置自动提交为false
                    conn = dataSource.getConnection();
                    conn.setAutoCommit(false);

                    String sql = "INSERT INTO test(oa_type, param) VALUES (?, ?)";
                    PreparedStatement pstmt = conn.prepareStatement(sql);
                    pstmt.setString(1, "value1");
                    pstmt.setString(2, "value2");
                    pstmt.executeUpdate();
                    conn.commit();
                } catch (Exception e) {
                    if (conn != null) {
                        try {
                            // 如果有异常发生,则回滚事务
                            conn.rollback();
                        } catch (SQLException ex) {
                            log.error("Failed to rollback transaction", ex);
                        }
                    }
                    throw new RuntimeException("Insert operation failed using JDBC", e);
                } finally {
                    if (conn != null) {
                        try {
                            // 关闭数据库连接
                            conn.close();
                        } catch (SQLException ex) {
                            log.error("Failed to close database connection", ex);
                        }
                    }
                }
                throw new RuntimeException();
            }else{
                throw new RuntimeException();
            }
        }
        throw new RuntimeException();
    }

}

4测试

添加注解,抛出指定异常

java 复制代码
@Transactional
    @GetMapping("/test")
    public void testLogin() throws OutApiException {
        OaTask oaTask = new OaTask();
        oaTask.setParam("test0314");
        oaTaskService.insertOaTask(oaTask);
        throw new OutApiException("123");
    }

断点进到切面里面了

并且表中只保留了异常数据,;另一个事务回滚了

相关推荐
better_liang6 小时前
每日Java面试场景题知识点之-消息队列MQ核心场景与实战
java·面试·kafka·消息队列·rabbitmq·rocketmq·mq
小江的记录本6 小时前
【JVM虚拟机】垃圾回收GC:四种引用类型:强引用、软引用、弱引用、虚引用(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·后端·python·spring·面试
小马爱打代码6 小时前
Spring源码 第四篇:Spring 5 源码深度拆解:AOP 全流程核心原理
java·后端·spring
better_liang6 小时前
每日Java面试场景题知识点之-SpringBoot启动流程
java·面试·springboot·源码解析·启动流程
RyFit7 小时前
Java + AI 实战:Spring AI 从入门到企业级落地
java·人工智能·spring
ServBay7 小时前
2026 Mac 本地大模型部署深度解析与混合架构指南
后端·macos·aigc
一拳一个娘娘腔7 小时前
【SRC漏洞挖掘系列】第10期:GraphQL & API 安全 —— 现代 API 的“裸奔”时代
后端·安全·graphql
ZhengEnCi8 小时前
01-如何监听接口调用情况?
java·spring boot·后端
JAVA面经实录9179 小时前
MyBatis学习体系
java·mybatis
java1234_小锋9 小时前
在 Spring AI 中如何实现函数调用(Function Calling)?请说明其基本原理和应用场景。
java·人工智能·spring