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");
    }

断点进到切面里面了

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

相关推荐
艾莉丝努力练剑13 分钟前
【C++:异常】C++ 异常处理完全指南:从理论到实践,深入理解栈展开与最佳实践
java·开发语言·c++·安全·c++11
武子康15 分钟前
Java-184 缓存实战:本地缓存 vs 分布式缓存(含 Guava/Redis 7.2)
java·redis·分布式·缓存·微服务·guava·本地缓存
小马爱打代码6 小时前
Spring Boot:模块化实战 - 保持清晰架构
java·spring boot·架构
小坏讲微服务7 小时前
SpringBoot4.0整合knife4j 在线文档完整使用
java·spring cloud·在线文档·knife4j·文档·接口文档·swagger-ui
8***Z897 小时前
springboot 异步操作
java·spring boot·mybatis
i***13247 小时前
Spring BOOT 启动参数
java·spring boot·后端
坚持不懈的大白7 小时前
后端:SpringMVC
java
IT_Octopus7 小时前
(旧)Spring Securit 实现JWT token认证(多平台登录&部分鉴权)
java·后端·spring
kk哥88997 小时前
Spring详解
java·后端·spring
S***26757 小时前
Spring Cloud Gateway 整合Spring Security
java·后端·spring